> 文章列表 > 《Spring系列》第7章 BeanDefinition

《Spring系列》第7章 BeanDefinition

《Spring系列》第7章 BeanDefinition

前言

BeanDefinition定义Bean配置元信息的接口,Spring会加载指定的Java类变成BeanDefinition对象, 然后根据此来创建Bean,那么下面就来详细介绍一下

1.类图

在这里插入图片描述

从类图中可以看出,BeanDefinition继承了AttributeAccessorBeanMetadataElement两个接口;

2.顶层接口

AttributeAccessor

可以看到里面的方法很好理解,对属性的增删改查

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

BeanDefinition

BeanDefinition定义很多接口方法,对应Bean属性,通过这种方式定义,\\、代表子类必须提供这些属性
在这里插入图片描述

3.抽象父类

AbstractBeanDefinition对这个接口进行了实现,它提供了一个BeanDefinition所需要具备的基本能力,它是最终全能BeanDefinition实现类的基类,下面展示了部分属性,对应的我们在bean中的各种属性

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessorimplements BeanDefinition, Cloneable {//	默认的SCOPE,默认是单例public static final String SCOPE_DEFAULT = "";//	不进行自动装配public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;//	根据Bean的名字进行自动装配,即autowired属性的值为bynamepublic static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;//	根据Bean的类型进行自动装配,调用setter函数装配属性,即autowired属性的值为byTypepublic static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;//	自动装配构造函数的形参,完成对应属性的自动装配,即autowired属性的值为byConstructorpublic static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;@Deprecatedpublic static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;//	不进行依赖检查public static final int DEPENDENCY_CHECK_NONE = 0;//	如果依赖类型为对象引用,则需要检查public static final int DEPENDENCY_CHECK_OBJECTS = 1;//	对简单属性的依赖进行检查public static final int DEPENDENCY_CHECK_SIMPLE = 2;//	对所有属性的依赖进行检查public static final int DEPENDENCY_CHECK_ALL = 3;//若Bean未指定销毁方法,容器应该尝试推断Bean的销毁方法的名字public static final String INFER_METHOD = "(inferred)";//	Bean的class对象或是类的全限定名@Nullableprivate volatile Object beanClass;//bean的作用范围,对应bean属性scope是单例@Nullableprivate String scope = SCOPE_DEFAULT;//是否是抽象,对应bean属性abstract 	默认不为抽象类private boolean abstractFlag = false;//是否延迟加载,对应bean属性lazy-init@Nullableprivate Boolean lazyInit;//	默认不进行自动装配private int autowireMode = AUTOWIRE_NO;//	默认不进行依赖检查private int dependencyCheck = DEPENDENCY_CHECK_NONE;//用来表示一个bean的实例化依靠另一个bean先实例化//这里只会存放<bean/>标签的depends-on属性或是@DependsOn注解的值@Nullableprivate String[] dependsOn;//autowire-candidate属性设置为false,这样容器在查找自动装配对象时,//将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,//但是该bean本身还是可以使用自动装配来注入其他bean的private boolean autowireCandidate = true;//自动装配时出现多个bean候选者时,将作为首选者,对应bean属性primaryprivate boolean primary = false;//用于记录Qualifier,对应子元素qualifierprivate final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();@Nullableprivate Supplier<?> instanceSupplier;//允许访问非公开的构造器和方法,程序设置private boolean nonPublicAccessAllowed = true;//是否以一种宽松的模式解析构造函数,默认为trueprivate boolean lenientConstructorResolution = true;//对应bean属性factory-bean 工厂类名@Nullableprivate String factoryBeanName;//对应bean属性factory-method 工厂方法名@Nullableprivate String factoryMethodName;//记录构造函数注入属性,对应bean属性constructor-arg@Nullableprivate ConstructorArgumentValues constructorArgumentValues;//普通属性集合@Nullableprivate MutablePropertyValues propertyValues;//方法重写的持有者,记录lookup-method、replaced-method元素private MethodOverrides methodOverrides = new MethodOverrides();//初始化方法,对应bean属性init-method@Nullableprivate String initMethodName;//销毁方法,对应bean属性destroy-method@Nullableprivate String destroyMethodName;//是否执行init-method,程序设置private boolean enforceInitMethod = true;//是否执行destroy-method,程序设置private boolean enforceDestroyMethod = true;//是否是用户定义的而不是应用程序本身定义的,创建AOP时候为true,程序设置private boolean synthetic = false;//定义这个bean的应用,APPLICATION:用户,INFRASTRUCTURE:完全内部使用,与用户无关private int role = BeanDefinition.ROLE_APPLICATION;//bean的描述信息@Nullableprivate String description;//bean的资源@Nullableprivate Resource resource;

4.子类实现

分类介绍

继承自AbstractBeanDefinition的全功能BeanDefinition实现类有 :

  1. RootBeanDefinition:Spring框架内部使用的,其他BeanDefinition都会转换为它
  2. GenericBeanDefinition:编程定义使用的,官方推荐使用
  3. ChildBeanDefinition:父子bean的时候使用,已逐步废弃
  4. ConfigurationClassBeanDefinition:写在@Configuration中的@Bean,会被注册成该BeanDefinition
  5. AnnotatedGenericBeanDefinition:通过注解方式引入的Bean,最常见的是@Import引入
  6. ScannedGenericBeanDefinition:通过扫描方式引入的Bean,那就是@ComponentScan

RootBeanDefinition

  1. 从Spring2.5开始,RootBeanDefinition仅作为运行时的BeanDefinition视图。
  2. 如果编程定义BeanDefinition,那么推荐使用GenericBeanDefinitionGenericBeanDefinition的优势在于,它允许动态定义父依赖项,而不是一个以”硬编码”定义BeanDefinition的角色。简单点说就是除了Spring框架,其它的编程都使用GenericBeanDefinition,功能都一样,而且还有有优点
  3. Spring初始化时,会用GenericBeanDefinition或是ConfigurationClassBeanDefinition(用@Bean注解注释的类)存储用户自定义的Bean,在初始化Bean时,又会将其转换为RootBeanDefinition。

上面的描述来自源码类注释,如下:

/* A root bean definition represents the merged bean definition that backs* a specific bean in a Spring BeanFactory at runtime. It might have been created* from multiple original bean definitions that inherit from each other,* typically registered as {@link GenericBeanDefinition GenericBeanDefinitions}.* A root bean definition is essentially the 'unified' bean definition view at runtime. <p>Root bean definitions may also be used for registering individual bean definitions* in the configuration phase. However, since Spring 2.5, the preferred way to register* bean definitions programmatically is the {@link GenericBeanDefinition} class.* GenericBeanDefinition has the advantage that it allows to dynamically define* parent dependencies, not 'hard-coding' the role as a root bean definition. @author Rod Johnson* @author Juergen Hoeller* @see GenericBeanDefinition* @see ChildBeanDefinition*/
@SuppressWarnings("serial")
public class RootBeanDefinition extends AbstractBeanDefinition {//....   
}

ChildBeanDefinition

基础介绍

当定义父子bean的时候,那么上面的RootBeanDefinition可以作为一个普通的BeanDefinition,也可以作为父bean,但是不能定义为子bean,那么这个时候就需要ChildBeanDefinition,而且不可以单独存在,必须要依赖一个父BeanDetintiont

下面只展示该类中的部分代码,看到子类只是扩展了一个parentName属性,存储父类

public class ChildBeanDefinition extends AbstractBeanDefinition {@Nullableprivate String parentName;// 省略构造方法    
}

逐步废弃

Spring2.5之后废弃。

个人觉得废弃的原因有如下:

  1. 父子bean有必要条件:子bean必须有父bean中的全部属性,父bean必须为抽象类
  2. 再实际开发中,没有人愿意去维护这个东西,有时间维护这个还不如写几行代码
  3. 用这个的一般是使用XML定义bean的老系统,但现在都转到注解开发了
    在这里插入图片描述

GenericBeanDefinition

基础介绍

通用的BeanDefinition,它的出现是为了替换掉ChildBeanDefinition,简单点说,之前为了实现父子bean,需要使用RootBeanDefinitionChildBeanDefinition,现在单独使用GenericBeanDefinition就可以实现

public class GenericBeanDefinition extends AbstractBeanDefinition {@Nullableprivate String parentName;// 省略构造方法...
}

测试用例

从网上拷贝下来的,只通过GenericBeanDefinition实现父子bean

package com.xjm.bean.definition;import com.xjm.model.Child;
import com.xjm.model.Root;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class GenericBeanDefinitionDemo {// 获取父beanpublic static GenericBeanDefinition getRootBeanDefinition() {GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();rootBeanDefinition.setBeanClass(Root.class);MutablePropertyValues propertyValues = new MutablePropertyValues();propertyValues.add("name", "root").add("description", "I am a rootBeanDefinition").add("isRoot", true);rootBeanDefinition.setPropertyValues(propertyValues);return rootBeanDefinition;}// 获取子beanpublic static GenericBeanDefinition getChildBeanDefinition() {GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();childBeanDefinition.setBeanClass(Child.class);MutablePropertyValues propertyValues = new MutablePropertyValues();propertyValues.add("parentName", "root");childBeanDefinition.setParentName("root");childBeanDefinition.setPropertyValues(propertyValues);return childBeanDefinition;}public static void main(String[] args) {// 1. 构建一个空的容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();GenericBeanDefinition rootBeanDefinition = getRootBeanDefinition();GenericBeanDefinition childBeanDefinition = getChildBeanDefinition();applicationContext.registerBeanDefinition("root", rootBeanDefinition);applicationContext.registerBeanDefinition("child", childBeanDefinition);applicationContext.refresh();Root root = applicationContext.getBean(Root.class);Child child = applicationContext.getBean(Child.class);System.out.println(root.toString());System.out.println(child.toString());}
}

在这里插入图片描述

ConfigurationClassBeanDefinition

基础介绍

  1. 通过 @Configuration + @Bean
  2. 在实际启动SpringBoot项目后,其实该ConfigurationClassBeanDefinition类型的BeanDefinition非常多,大部分都是,原因:SpringBoot的自动化配置特点,会引入非常多的依赖,这些依赖注入自己的组件都是通过配置类的方式
  3. 这个bean创建是需要调用到Configuration Class中定义bean的方法。
  4. 如下源码所示,它是一个静态内部类,代码很少,扩展了2个属性:annotationMetadatafactoryMethodMetadata。扩展的属性有啥用?下面介绍
  5. factoryMethodMetadata存储方法信息。用处:通过@Bean定义的类,当获取时,就不是通过构造方法了,而是类似于工厂方法模式直接调用该方法,而且 如果@Bean没有指定 Bean 的名字,默认会用方法的名字命名 Bean。那么factoryMethodMetadata就存储方法的各种信息
  6. annotationMetadata存储@Bean注解属性。作用:@Bean里面也有很多属性,创建的时候肯定需要用到
// 静态内部类
private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {// 存储@Bean注解的信息private final AnnotationMetadata annotationMetadata;// 存储@Bean方法的信息private final MethodMetadata factoryMethodMetadata;// 省略...   
}

Debug测试

编写了一个小测试,看到结果确实是对应类型
在这里插入图片描述

看到下图很多都是SpringBoot自动配置引入的Bean,基本都是ConfigurationClassBeanDefinition
在这里插入图片描述

AnnotatedGenericBeanDefinition

它用来定义通过注解引入的Bean,最常见的肯定就是@Import引入

截图解释

下面截图解释:

  1. SpringBoot的启动类肯定是配置类,在启动的时候单独注入到容器,肯定是AnnotatedGenericBeanDefinition
  2. 下图中的aspectConfig就变成了ScannedGenericBeanDefinition,为啥呢?因为它不是单独注册的,它是启动类上的@ComponentScan注解扫描到的配置类,所以就会是该类型
  3. 下图中的qqqq,这个就是正常的AnnotatedGenericBeanDefinition,为啥呢?首先我把它和启动类分成不同层级的包,启动类扫描不到,然后再启动类上引入该配置类@Import(TestConfig.class),就会是想要的结果
  4. 当然是普通的Bean,通过该注解引入,也是是该类型

在这里插入图片描述

ScannedGenericBeanDefinition

通过名字可以看出是通过扫描的方式注入到Bean中,扫描需要是用到@ComponentScan

5.BeanDefinitionHolder

在扫描Bean的时候,会用到一个辅助类BeanDefinitionHolder,顾名思义就是对BeanDefinition的持有,通过包含其名称和别名

这个一般在解析@ComponentScan的时候用到,会把扫描到的类封装成BeanDefinitionHolder,把各种信息都存进去,就是一个辅助类很好理解

public class BeanDefinitionHolder implements BeanMetadataElement {private final BeanDefinition beanDefinition;// Bean名称private final String beanName;// 别名@Nullableprivate final String[] aliases;}

有,通过包含其名称和别名

这个一般在解析@ComponentScan的时候用到,会把扫描到的类封装成BeanDefinitionHolder,把各种信息都存进去,就是一个辅助类很好理解

public class BeanDefinitionHolder implements BeanMetadataElement {private final BeanDefinition beanDefinition;// Bean名称private final String beanName;// 别名@Nullableprivate final String[] aliases;}