《Spring系列》第7章 BeanDefinition

前言

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

1.类图

在这里插入图片描述

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

2.顶层接口

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();
}

BeanDefinition

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

3.抽象父类

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

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
	//	默认的SCOPE,默认是单例
	public static final String SCOPE_DEFAULT = "";
	//	不进行自动装配
	public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
	//	根据Bean的名字进行自动装配,即autowired属性的值为byname
	public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
	//	根据Bean的类型进行自动装配,调用setter函数装配属性,即autowired属性的值为byType
	public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
	//	自动装配构造函数的形参,完成对应属性的自动装配,即autowired属性的值为byConstructor
	public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
	@Deprecated
	public 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对象或是类的全限定名
	@Nullable
	private volatile Object beanClass;
	//bean的作用范围,对应bean属性scope是单例
	@Nullable
	private String scope = SCOPE_DEFAULT;
	//是否是抽象,对应bean属性abstract 	默认不为抽象类
	private boolean abstractFlag = false;
	//是否延迟加载,对应bean属性lazy-init
	@Nullable
	private Boolean lazyInit;
	//	默认不进行自动装配
	private int autowireMode = AUTOWIRE_NO;
	//	默认不进行依赖检查
	private int dependencyCheck = DEPENDENCY_CHECK_NONE;
	//用来表示一个bean的实例化依靠另一个bean先实例化
	//这里只会存放<bean/>标签的depends-on属性或是@DependsOn注解的值
	@Nullable
	private String[] dependsOn;
	//autowire-candidate属性设置为false,这样容器在查找自动装配对象时,
  	//将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,
    //但是该bean本身还是可以使用自动装配来注入其他bean的
	private boolean autowireCandidate = true;
	//自动装配时出现多个bean候选者时,将作为首选者,对应bean属性primary
	private boolean primary = false;
	//用于记录Qualifier,对应子元素qualifier
	private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
	@Nullable
	private Supplier<?> instanceSupplier;
	//允许访问非公开的构造器和方法,程序设置
	private boolean nonPublicAccessAllowed = true;
	//是否以一种宽松的模式解析构造函数,默认为true
	private boolean lenientConstructorResolution = true;
	//对应bean属性factory-bean 工厂类名
	@Nullable
	private String factoryBeanName;
	//对应bean属性factory-method 工厂方法名
	@Nullable
	private String factoryMethodName;
	//记录构造函数注入属性,对应bean属性constructor-arg
	@Nullable
	private ConstructorArgumentValues constructorArgumentValues;
	//普通属性集合
	@Nullable
	private MutablePropertyValues propertyValues;
	//方法重写的持有者,记录lookup-method、replaced-method元素
	private MethodOverrides methodOverrides = new MethodOverrides();
	//初始化方法,对应bean属性init-method
	@Nullable
	private String initMethodName;
	//销毁方法,对应bean属性destroy-method
	@Nullable
	private 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的描述信息
	@Nullable
	private String description;
	//bean的资源
	@Nullable
	private 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 {

   @Nullable
   private String parentName;
    
    
   // 省略构造方法    
}
逐步废弃

Spring2.5之后废弃。

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

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

GenericBeanDefinition

基础介绍

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

public class GenericBeanDefinition extends AbstractBeanDefinition {

   @Nullable
   private 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 {

    // 获取父bean
    public 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;
    }

    // 获取子bean
    public 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;

   // 别名
   @Nullable
   private final String[] aliases;
    
    
}

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

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

public class BeanDefinitionHolder implements BeanMetadataElement {

   private final BeanDefinition beanDefinition;

   // Bean名称
   private final String beanName;

   // 别名
   @Nullable
   private final String[] aliases;
    
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为人师表好少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值