乐趣区

关于spring:七种-BeanDefinition各显其能

聚沙成塔!人不知; 鬼不觉 Spring 源码曾经间断更了两个月啦,视频也录制了不少了,对 Spring 源码剖析感兴趣的小伙伴戳这里哦 Spring 源码应该怎么学?~

明天咱们持续来看 Spring 源码中一个十分重要的概念:BeanDefinition。

1.BeanDefinition

在 Spring 容器中,咱们宽泛应用的是一个一个的 Bean,BeanDefinition 从名字上就能够看出是对于 Bean 的定义。

事实上就是这样,咱们在 XML 文件中配置的 Bean 的各种属性,亦或者用注解定义进去的 Bean 的各种属性,在真正生成 Bean 间接,咱们须要先对这些设置的属性进行解析,解析的后果须要有一个对象来承载,很显著,这个对象就是 BeanDefinition。

无论是通过 XML 中定义的 Bean 属性还是通过 Java 代码定义的 Bean 属性,都会先加载到 BeanDefinition 上,而后通过 BeanDefinition 来生成一个 Bean,从这个角度来说,BeanDefinition 和 Bean 的关系有点相似于类和对象的关系,BeanDefinition 是模板,Bean 是模板具体化之后的产物。

要了解 BeanDefinition,咱们从 BeanDefinition 的继承关系开始看起。

BeanDefinition 是一个接口,继承自 BeanMetadataElement 和 AttributeAccessor 接口。

  • BeanMetadataElement:该接口只有一个办法 getSource,该办法返回 Bean 的起源。
  • AttributeAccessor:该接口次要标准了问任意对象元数据的办法。

咱们来看下 AttributeAccessor:

public interface AttributeAccessor {void setAttribute(String name, @Nullable Object value);
    @Nullable
    Object getAttribute(String name);
    @Nullable
    Object removeAttribute(String name);
    boolean hasAttribute(String name);
    String[] attributeNames();
}

这里定义了元数据的拜访接口,具体的实现则是 AttributeAccessorSupport,这些数据采纳 LinkedHashMap 进行存储。

这是 BeanDefinition 所继承的两个接口。接下来咱们来看下 BeanDefinition 接口:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;
    void setParentName(@Nullable String parentName);
    @Nullable
    String getParentName();
    void setBeanClassName(@Nullable String beanClassName);
    @Nullable
    String getBeanClassName();
    void setScope(@Nullable String scope);
    @Nullable
    String getScope();
    void setLazyInit(boolean lazyInit);
    boolean isLazyInit();
    void setDependsOn(@Nullable String... dependsOn);
    @Nullable
    String[] getDependsOn();
    void setAutowireCandidate(boolean autowireCandidate);
    boolean isAutowireCandidate();
    void setPrimary(boolean primary);
    boolean isPrimary();
    void setFactoryBeanName(@Nullable String factoryBeanName);
    @Nullable
    String getFactoryBeanName();
    void setFactoryMethodName(@Nullable String factoryMethodName);
    @Nullable
    String getFactoryMethodName();
    ConstructorArgumentValues getConstructorArgumentValues();
    default boolean hasConstructorArgumentValues() {return !getConstructorArgumentValues().isEmpty();}
    MutablePropertyValues getPropertyValues();
    default boolean hasPropertyValues() {return !getPropertyValues().isEmpty();}
    void setInitMethodName(@Nullable String initMethodName);
    @Nullable
    String getInitMethodName();
    void setDestroyMethodName(@Nullable String destroyMethodName);
    @Nullable
    String getDestroyMethodName();
    void setRole(int role);
    int getRole();
    void setDescription(@Nullable String description);
    @Nullable
    String getDescription();
    ResolvableType getResolvableType();
    boolean isSingleton();
    boolean isPrototype();
    boolean isAbstract();
    @Nullable
    String getResourceDescription();
    @Nullable
    BeanDefinition getOriginatingBeanDefinition();}

BeanDefinition 中的办法尽管多,然而联合咱们平时在 XML/Java 中的配置,这些办法其实都很好了解:

  1. 首先一开始定义了两个变量用来形容 Bean 是不是单例的,前面的 setScope/getScope 办法能够用来批改 / 获取 scope 属性。
  2. ROLE_xxx 用来形容一个 Bean 的角色,ROLE_APPLICATION 示意这个 Bean 是用户本人定义的 Bean;ROLE_SUPPORT 示意这个 Bean 是某些简单配置的撑持局部;ROLE_INFRASTRUCTURE 示意这是一个 Spring 外部的 Bean,通过 setRole/getRole 能够批改。
  3. setParentName/getParentName 用来配置 parent 的名称,这块可能有的小伙伴应用较少,这个对应着 XML 中的 <bean parent=""> 配置,在之前的视频中松哥曾经和大家讲过了 Spring 中 parent 的应用了。
  4. setBeanClassName/getBeanClassName 这个就是配置 Bean 的 Class 全门路,对应 XML 中的 <bean class=""> 配置。
  5. setLazyInit/isLazyInit 配置 / 获取 Bean 是否懒加载,这个对应了 XML 中的 <bean lazy-init=""> 配置。
  6. setDependsOn/getDependsOn 配置 / 获取 Bean 的依赖对象,这个对应了 XML 中的 <bean depends-on=""> 配置。
  7. setAutowireCandidate/isAutowireCandidate 配置 / 获取 Bean 是否是主动拆卸,对应了 XML 中的 <bean autowire-candidate=""> 配置。
  8. setPrimary/isPrimary 配置 / 获取以后 Bean 是否为首选的 Bean,对应了 XML 中的 <bean primary=""> 配置。
  9. setFactoryBeanName/getFactoryBeanName 配置 / 获取 FactoryBean 的名字,对应了 XML 中的 <bean factory-bean=""> 配置,factory-bean 松哥在之前的视频中讲过,小伙伴们能够参考这里:Spring 源码应该怎么学?。
  10. setFactoryMethodName/getFactoryMethodName 和上一条成对呈现的,对应了 XML 中的 <bean factory-method=""> 配置,不再赘述。
  11. getConstructorArgumentValues 返回该 Bean 构造方法的参数值。
  12. hasConstructorArgumentValues 判断上一条是否是空对象。
  13. getPropertyValues 这个是获取一般属性的汇合。
  14. hasPropertyValues 判断上一条是否为空对象。
  15. setInitMethodName/setDestroyMethodName 配置 Bean 的初始化办法、销毁办法。
  16. setDescription/getDescription 配置 / 返回 Bean 的形容。
  17. isSingleton Bean 是否为单例。
  18. isPrototype Bean 是否为原型。
  19. isAbstract Bean 是否形象。
  20. getResourceDescription 返回定义 Bean 的资源形容。
  21. getOriginatingBeanDefinition 如果以后 BeanDefinition 是一个代理对象,那么该办法能够用来返回原始的 BeanDefinition。

这个就是 BeanDefinition 的定义以及它里边办法的含意。

2.BeanDefinition 实现类

下面只是 BeanDefinition 接口的定义,BeanDefinition 还领有诸多实现类,咱们也来大抵理解下。

先来看一张继承关系图:

这么多实现类看着有点目迷五色,不过搞清楚了每一个接口和类的作用,再看就很容易了。

2.1 AbstractBeanDefinition

AbstractBeanDefinition 是一个抽象类,它依据 BeanDefinition 中定义的接口提供了相应的属性,并实现了 BeanDefinition 中定义的一部分办法。BeanDefinition 中本来只是定义了一系列的 get/set 办法,并没有提供对应的属性,在 AbstractBeanDefinition 中将所有的属性定义进去了。

前面其余的实现类也基本上都是在 AbstractBeanDefinition 的根底上实现的。

2.2 RootBeanDefinition

这是一个比拟罕用的实现类,对应了个别的元素标签。

2.3 ChildBeanDefinition

能够让子 BeanDefinition 定义领有从父 BeanDefinition 那里继承配置的能力,如果子 Bean 从父 Bean 获取配置,能够参考松哥之前的这篇文章:Spring BeanDefinition 也分父子?。

2.4 GenericBeanDefinition

GenericBeanDefinition 是从 Spring2.5 当前新退出的 BeanDefinition 实现类。GenericBeanDefinition 能够动静设置父 Bean,同时兼具 RootBeanDefinition 和 ChildBeanDefinition 的性能,因而,自从有了 GenericBeanDefinition 之后,RootBeanDefinition 和 ChildBeanDefinition 当初绝对就用的少了。

2.5 AnnotatedBeanDefinition

这个示意注解类型 BeanDefinition,用于示意通过注解配置的 Bean 定义。通过 AnnotatedBeanDefinition,咱们能够获取到被注解的 Bean 的相干信息,包含注解类型、属性值、办法等。这个接口提供了一种不便的形式来解决通过注解形式配置的 Bean,并且能够在运行时动静地获取和操作这些注解信息,当然,这是一个接口,它有三个实现类,别离是 AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition 以及 ConfigurationClassBeanDefinition。

2.6 AnnotatedGenericBeanDefinition

作为系统配置类的类会解析为 AnnotatedGenericBeanDefinition。

2.7 ScannedGenericBeanDefinition

这个是通过包扫描主动注册的 Bean,就会被解析为 ScannedGenericBeanDefinition。

2.8 ConfigurationClassBeanDefinition

这是一个公有的外部类。咱们通过 @Bean 注解定义的 Bean,最终会被解析为 ConfigurationClassBeanDefinition。

2.9 ClassDerivedBeanDefinition

ClassDerivedBeanDefinition 的作用是扩大并形容一个类派生的 Bean 的元数据。它是 AnnotatedBeanDefinition 接口的一个实现类,在 Spring 框架中用于示意通过类派生形式配置的 Bean 定义。

2.10 CreateFromClassBeanDefinition

这个是依照类型创立 Bean 的时候会用到。

差不多就这么多了,大部分咱们日常开发中其实都用不上,接下来松哥通过几个具体的案例来和小伙伴们演示这些 BeanDefinition 的具体用法。前面的文章,咱们再来剖析这些 BeanDefinition 在 Spring 源码中是如何利用的。

3. 实际

接下来我通过几个具体的案例来和小伙伴们演示各种不同的 BeanDefinition 的用法,明天我次要和小伙伴们演示咱们纯手动应用 BeanDefinition,而后剖析一下咱们平时的配置实质上应用的 BeanDefinition 是哪一个,明天咱们先不去源码剖析,单纯的就看看成果。

3.1 GenericBeanDefinition

先来看 GenericBeanDefinition,这个性能绝对比拟全,兼具 RootBeanDefinition 和 ChildBeanDefinition 的能力。

先来看一个简略用法:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(User.class);
MutablePropertyValues pValues = new MutablePropertyValues();
pValues.add("username", "javaboy");
bd.setPropertyValues(pValues);
beanFactory.registerBeanDefinition("user", bd);
User user = beanFactory.getBean("user", User.class);
System.out.println("user =" + user);

小伙伴们看到,咱们这里向 Spring 容器注册了一个 GenericBeanDefinition 类型的 BeanDefinition,GenericBeanDefinition 中蕴含了具体的 class 以及 Bean 的各个属性值。

如果咱们在 Bean 定义的时候,想要应用继承个性,也能够应用 GenericBeanDefinition:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition parentBD = new GenericBeanDefinition();
GenericBeanDefinition childBD = new GenericBeanDefinition();
parentBD.setBeanClass(Animal.class);
MutablePropertyValues pValues = new MutablePropertyValues();
pValues.add("name", "小黄");
parentBD.setPropertyValues(pValues);
childBD.setBeanClass(Dog.class);
childBD.setParentName("animal");
beanFactory.registerBeanDefinition("animal", parentBD);
beanFactory.registerBeanDefinition("dog", childBD);
Dog dog = beanFactory.getBean("dog", Dog.class);
System.out.println("dog =" + dog);

这次我间接定义了两个 GenericBeanDefinition,一个作为 parent,另外一个作为 child,为 child 设置 parentName,则 child 能够继承 parent 中的属性。下面的案例中,最终打印进去 dog 的 name 属性就是 小黄 ,这块小伙伴们能够参考松哥之前的文章:Spring BeanDefinition 也分父子?。

咱们平时通过 XML 文件定义的 Bean,最终解析后就是 GenericBeanDefinition。

例如上面这样:

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.javaboy.demo.User" id="user"></bean>
</beans>

加载 XML 文件,创立容器:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition bd = ctx.getBeanFactory().getBeanDefinition(beanDefinitionName);
    System.out.println(beanDefinitionName + ">>>" + bd.getClass());
}

最终打印后果如下:

这个也好了解,毕竟咱们在 XML 中配置的时候,可能存在 parent,也可能不存在,用 GenericBeanDefinition 就可能应答各种状况。

3.2 RootBeanDefinition/ChildBeanDefinition

这两个惯例的性能其实都有,然而 RootBeanDefinition 个别能够用来做 parent,不能用作 child,即给 RootBeanDefinition 不能配置 parentName 属性。强行设置会抛出如下异样:

@Override
public void setParentName(@Nullable String parentName) {if (parentName != null) {throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
    }
}

ChildBeanDefinition 则既能够做 parent 也能够做 child,然而 ChildBeanDefinition 在应用的应用必须指定 parent,即便 ChildBeanDefinition 作为 parent,也必须指定 parent,所以 ChildBeanDefinition 在应用的过程中有一点点局限性,因而目前被 GenericBeanDefinition 代替了。

来看一个简略的案例:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
RootBeanDefinition parentBD = new RootBeanDefinition();
parentBD.setBeanClass(Animal.class);
MutablePropertyValues pValues = new MutablePropertyValues();
pValues.add("name", "小黄");
parentBD.setPropertyValues(pValues);
ChildBeanDefinition childBD = new ChildBeanDefinition("animal");
childBD.setBeanClass(Dog.class);
beanFactory.registerBeanDefinition("animal", parentBD);
beanFactory.registerBeanDefinition("dog", childBD);
Dog dog = beanFactory.getBean("dog", Dog.class);
System.out.println("dog =" + dog);

3.3 AnnotatedGenericBeanDefinition

对于应用 @Configuration 注解标记的类,最终解析进去的 BeanDefinition 就是 AnnotatedGenericBeanDefinition。例如我有一个配置类如下:

@Configuration
public class JavaConfig {}

加载配置类并启动容器:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition beanDefinition = ctx.getBeanFactory().getBeanDefinition(beanDefinitionName);
    System.out.println(beanDefinitionName + ">>>" + beanDefinition.getClass());
}

最终打印后果如下:

3.4 ScannedGenericBeanDefinition

这个是那些通过包扫描注册到 Spring 容器中的 Bean,在一开始定义进去的 BeanDefinition 就是 ScannedGenericBeanDefinition。

例如我有如下 Bean:

@Service
public class UserService {}

而后配置包扫描:

@Configuration
@ComponentScan
public class JavaConfig {}

启动容器:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition beanDefinition = ctx.getBeanFactory().getBeanDefinition(beanDefinitionName);
    System.out.println(beanDefinitionName + ">>>" + beanDefinition.getClass());
}

最终打印后果如下:

3.5 ConfigurationClassBeanDefinition

当咱们通过 @Bean 注解去定义 Bean 的时候,那么被 @Bean 注解标记的类就会被解析为 ConfigurationClassBeanDefinition。

例如上面这个例子:

@Configuration
public class JavaConfig {
    @Bean
    User user() {return new User();
    }
}

查看解析后的 BeanDefinition 如下:

3.6 CreateFromClassBeanDefinition

这个其实用的少,然而咱么既然讲到 Spring,松哥也说两句。

这个是当咱们想要创立一个对象,咱们心愿这个对象可能主动走一遍 Spring 中的各种后置处理器的时候,那么能够调用 createBean 办法,该办法外部应用了 CreateFromClassBeanDefinition。

例如如下代码:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
User user = beanFactory.createBean(User.class);
System.out.println("user =" + user);

应用这种形式去创立一个 Bean,这个 Bean 会走一遍 Spring 中 Bean 的后置处理器,其中,createBean 办法的外部就应用了 CreateFromClassBeanDefinition。

3.7 ClassDerivedBeanDefinition

ClassDerivedBeanDefinition 和 CreateFromClassBeanDefinition 其实比拟像,差异在于二者解决构造方法的形式不同。

而且 ClassDerivedBeanDefinition 是一个相当冷门的 BeanDefinition,在 GenericApplicationContext 的实现类中,能够应用 GenericXmlApplicationContext、StaticApplicationContext 或者 GenericGroovyApplicationContext,只有这三个类中 registerBean 办法用到了 ClassDerivedBeanDefinition,咱们常见的 AnnotationConfigApplicationContext 因为办法重写的缘故并未应用 ClassDerivedBeanDefinition。

StaticApplicationContext ctx = new StaticApplicationContext();
ctx.registerBean(User.class,()->{User user = new User();
    user.setUsername("javaboy");
    return user;
});
User user = ctx.getBean(User.class);
System.out.println("user =" + user);
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition bd = ctx.getBeanDefinition(beanDefinitionName);
    System.out.println(beanDefinitionName + ">>>" + bd.getClass());
}

咱们能够调用 registerBean 办法向 Spring 容器中注入一个 Bean,该办法第二个参数是一个 Bean 的生产者,如果不指定生产者,那么这个办法最终就是通过第一个参数反射创立 Bean,registerBean 办法的外部就是应用了 ClassDerivedBeanDefinition。

好啦,BeanDefinition 一共就是这七种,接下来我会通过几篇文章和大家重点介绍 GenericBeanDefinition、AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition 以及 ConfigurationClassBeanDefinition 这四种最为常见的 BeanDefinition。

退出移动版