0
点赞
收藏
分享

微信扫一扫

Spring学习(二)Bean的设计图-BeanDefinition

拾光的Shelly 2021-09-25 阅读 58
spring

版本说明

本文的Spring版本为5.2.12.RELEASE

什么是BeanDefinition

当我们在spring中表示一个Bean时,我们通常会使用XML配置文件或者注解等方式来表示。通过配置文件或者注解中的信息,容器就能按照我们的定义来创建Bean,并为之提供不同的特性。这些XML或者注解中的信息最后都将被解析成对应的BeanDefinition,容器在创建这些Bean时,它的依据就是BeanDefinition中的信息。如果使用现实生活中的例子来说明,BeanDefinition就好像产品的设计图,而容器就是工厂,工厂(spring)按照产品的设计图(BeanDefinition)来生产产品(Bean实例)。例如下面这段XML文件中的内容,我们定义一个Book实例如下:

<bean id="book" class="com.buydeem.model.Book">
    <property name="bookName" value="钢铁是怎样炼成的"/>
    <property name="author" value="尼古拉·奥斯特洛夫斯基"/>
</bean>

Spring在处理上面的内容时会将使用BeanDefinition来保存其中的信息。当然Spring中还有其他实现,例如:注解、properties文件等等。

public class BeanDefinitionDemo {
    public static void main(String[] args){
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("spring-bean-definition.xml");
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("book");
        System.out.println(beanDefinition);
    }
}

上面就是实例就是读取spring-bean-definition.xml文件中的内容将其转化成BeanDefinition。

BeanDefinition包含哪些信息

在Spring的官方文档中Bean Overview中有有详细说明。主要包括以下内容:

Property Explained in…​
Class Instantiating Beans
Name Naming Beans
Scope Bean Scopes
Constructor arguments Dependency Injection
Properties Dependency Injection
Autowiring mode Autowiring Collaborators
Lazy initialization mode Lazy-initialized Beans
Initialization method Initialization Callbacks
Destruction method Destruction Callbacks

从上面可以看出,我们平常在使用XML或者注解时定义的内容都包括在上面的表格中。

如何创建BeanDefinition

不管是读取XML文件还是注解等方式构建BeanDefinition,最后底层都是基于API方式来创建BeanDefinition实例。下面使用读取XML和Builder模式构建BeanDefinition:

public class BeanDefinitionDemo {
    public static void main(String[] args){
        //xml构建
        xmlBeanDefinition();
        //builder构建
        builderBeanDefinition();
    }
    public static void xmlBeanDefinition(){
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("spring-bean-definition.xml");
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("book");
        System.out.println(beanDefinition);
        Book book = beanFactory.getBean(Book.class);
        System.out.println(book);
    }
    public static void builderBeanDefinition(){
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Book.class)
                .addPropertyValue("bookName","钢铁是怎样炼成的")
                .addPropertyValue("author","尼古拉·奥斯特洛夫斯基")
                .getBeanDefinition();
        System.out.println(beanDefinition);
        beanFactory.registerBeanDefinition("user",beanDefinition);
        System.out.println(beanFactory.getBean("user"));
    }
}

最后的结果如下:

Generic bean: class [com.buydeem.model.Book]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring-bean-definition.xml]
Book{bookName='钢铁是怎样炼成的', author='尼古拉·奥斯特洛夫斯基'}
Generic bean: class [com.buydeem.model.Book]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Book{bookName='钢铁是怎样炼成的', author='尼古拉·奥斯特洛夫斯基'}

构建的BeanDefinition的方式很多,不仅仅值限于上面的方式。

如何注册BeanDefinition

创建Bean需要BeanDefinition,解析完配置或者注解生成的BeanDefinition最后会被注册。BeanDefinitionRegistry就是最底层的关于BeanDefinition注册相关的接口。该接口主要提供了对BeanDefinition管理相关的方法,简单的说就是定义了增删查等方法。常见的IOC容器实现类几乎都直接或者间接的实现了该接口。这也说明IOC容器具有管理BeanDefinition的功能。

public interface BeanDefinitionRegistry extends AliasRegistry {
   //注册BeanDefinition
   void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
         throws BeanDefinitionStoreException;
   //删除BeanDefinition
   void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
   //获取BeanDefinition
   BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
   //是否包含BeanDefinition
   boolean containsBeanDefinition(String beanName);
   //获取BeanDefinition的名字数组
   String[] getBeanDefinitionNames();
   //获取已经注册的BeanDefinition数量
   int getBeanDefinitionCount();
   //确定给定的bean名称是否被使用
   boolean isBeanNameInUse(String beanName);
}

常见的实现

RootBeanDefinition和ChildBeanDefinition

这两个实现通常可以搭配使用,从而在一定程度上减少重复配置。例如现在我们有一个Mike类,该类的定义如下:

public class Mike {
    private String color;
    private String source;
    private String country;
}

现在我们需要创建多个Mike类型的实例,而这些实例中大多数属性都是一致的,只有少部分属性是特殊的。如下面spring-root-child-bean-definition.xml所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--父模板-->
    <bean id="root" abstract="true">
        <property name="color" value="白色"/>
        <property name="source" value="牛"/>
    </bean>
    <!--子模板继承父模板-->
    <bean id="yili" class="com.buydeem.model.Mike" parent="root">
        <property name="country" value="中国"/>
    </bean>
    <!--子模板继承父模板-->
    <bean id="quecao" class="com.buydeem.model.Mike" parent="root">
        <property name="country" value="美国"/>
    </bean>
</beans>

在上面的XML配置文件中,我们定义了两个实例Bean,分别为yili和quecao。我们定义中它的颜色和来源都是一样的,但是品牌的国籍不同,一个来自中国一个来自美国。使用测试代码如下:

public static void xmlRootAndChildBeanDefinition(){
    //创建IOC容器
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    //读取配置文件
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("spring-root-child-bean-definition.xml");
    //获取Bean
    Mike yiliMike = (Mike) beanFactory.getBean("yili");
    Mike quecaoMike = (Mike) beanFactory.getBean("quecao");
    System.out.println(yiliMike);
    System.out.println(quecaoMike);
}

上面的方式是使用XML配置的,我们同样是可以使用API来完成上面XML的配置,代码如下:

public static void apiRootAndChildBeanDefinition(){
    //创建容器
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    //创建Root模板BeanDefinition
    MutablePropertyValues rootProperties = new MutablePropertyValues()
            .add("color","白色")
            .add("source","牛");
    RootBeanDefinition root = new RootBeanDefinition();
    root.setPropertyValues(rootProperties);
    root.setAbstract(true);
    beanFactory.registerBeanDefinition("root",root);
    //创建yili
    MutablePropertyValues yiliProperties = new MutablePropertyValues()
            .add("country","中国");
    ChildBeanDefinition yiliBeanDefinition = new ChildBeanDefinition("root", Mike.class,null,yiliProperties);
    beanFactory.registerBeanDefinition("yili",yiliBeanDefinition);
    //创建quecao
    MutablePropertyValues quecaoProperties = new MutablePropertyValues()
            .add("country","美国");
    ChildBeanDefinition quecaoBeanDefinition = new ChildBeanDefinition("root", Mike.class,null,quecaoProperties);
    beanFactory.registerBeanDefinition("quecao",quecaoBeanDefinition);
    //获取Bean
    Mike yiliMike = (Mike) beanFactory.getBean("yili");
    Mike quecaoMike = (Mike) beanFactory.getBean("quecao");
    System.out.println(yiliMike);
    System.out.println(quecaoMike);
}

GenericBeanDefinition

该类是Spring2.5之后推出的一个实现类,他既可以当做普通BeanDefinition来使用,同样也能当做RootBeanDefinition来使用。

ConfigurationClassBeanDefinition

该类是ConfigurationClassBeanDefinitionReader中的一个内部类,我们在spring中使用@Bean注解来定义Bean时生成的BeanDefinition就是这种类型,代码如下:

public static void configurationClassBeanDefinition(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    //BeanDefinitionDemo类为配置类
    context.register(BeanDefinitionDemo.class);
    context.refresh();
    Mike yili = (Mike) context.getBean("yili");
    System.out.println(yili);
    BeanDefinition definition = context.getBeanDefinition("yili");
    System.out.println(definition.getClass());
}
@Bean
public Mike yili(){
    Mike mike = new Mike();
    mike.setColor("白色");
    mike.setSource("牛");
    mike.setCountry("中国");
    return mike;
}

AnnotatedGenericBeanDefinition

从名称可以看出处理注解相关的BeanDefinition,例如我们下面的MyComponent类中,我们可以设置它的名称,scope,是不是primary等等信息。

public static void annotatedGenericBeanDefinition(){
    DefaultListableBeanFactory  beanFactory = new DefaultListableBeanFactory();
    AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
    reader.register(MyComponent.class);
    MyComponent myComponent1 = beanFactory.getBean(MyComponent.class);
    MyComponent myComponent2 = beanFactory.getBean(MyComponent.class);
    System.out.println(myComponent1 == myComponent2);
    BeanDefinition definition = beanFactory.getBeanDefinition("myComponent_1");
    System.out.println(definition.getClass());
    System.out.println(definition.getScope());
}

其中MyComponent类的定义如下:

@Component(value = "myComponent_1")
@Primary
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyComponent{
}

最后的打印结果如下所示:

false
class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
prototype

ScannedGenericBeanDefinition

通过名称就能知道该BeanDefinition与扫描相关。我们在spring 中可以通过指定包名,然后通过扫描的方式将@Component,@Service,@Repository等相关注解标记的类注册成Spring Bean。直接看下面代码:

BookDao定义如下:

package com.buydeem.component;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
}

BookService定义如下:

package com.buydeem.component;
import org.springframework.stereotype.Service;
@Service
public class BookService {
}

示例代码如下:

public static void scannedGenericBeanDefinition() {
    ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
    Set<BeanDefinition> beanDefinitions = provider.findCandidateComponents("com.buydeem.component");
    for (BeanDefinition beanDefinition : beanDefinitions) {
        System.out.println(beanDefinition.getBeanClassName() + " = " + beanDefinition.getClass());
    }
}

打印结果如下:

com.buydeem.component.BookDao = class org.springframework.context.annotation.ScannedGenericBeanDefinition
com.buydeem.component.BookService = class org.springframework.context.annotation.ScannedGenericBeanDefinition

总结

上面的东西看完之后主要要明白下面几点:

  1. BeanDefinition就是用来描述一个Bean的特性的,容器创建Bean就是根据BeanDefinition中记录的信息来创建的。
  2. 定义BeanDefinition的相关配置文件或者注解最后都将转成的BeanDefinition。
  3. Spring中IOC容器基本上都具有对BeanDefinition的管理功能。

明白上面几点,有利于对spring中整体的了解,便于下一步学习。

举报

相关推荐

0 条评论