BeanDefinition接口

333 阅读8分钟

前言

BeanDefinition 是 Spring 在创建 Bean 时的模板,这个模板中保存了创建 Bean 的各项参数,比如 Bean 的类型、Bean 的创建方式、Bean 创建时需要的参数列表、Bean 的生命周期参数、Bean 的初始化方法等。想要深刻的理解 BeanDefinition,仅仅阐述 BeanDefinition 接口的各个方法、各种实现是不太足够的,需要在 Spring 实例化一个 Bean 的具体细节中才能真正的说透。本文会介绍 BeanDefinition 接口的各个方法、基本的抽象实现和各种扩展实现以及他们各自使用的场景,后续的文章会介绍 Spring 是如何加载、创建 BeanDefinition、如何合并 BeanDefinition,并利用 BeanDefinition 来创建 Bean 实例的。

BeanDefinition 接口

BeanDefinition 是一个接口,全限定名为 org.springframework.beans.factory.config.BeanDefinition,此接口继承了 AttributeAccessor 和 BeanMetadataElement 接口,代码如下

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement{
  /** ... */
}
  • AttributeAccessor 属性访问器接口
    • 用于存储各种属性映射(属性名和属性值),是 KV 结构数据,可以简单的理解为一个 Map,一个存储各种杂数据的字典
  • BeanMetadataElement Bean 元数据元素接口
    • 只有一个方法 getSource,用于记录一个对象,这个对象是配置 Bean 的地方,比如一个 Xml 资源文件,一个 Java 类等

BeanDefinition 的方法

下面看下 BeanDefinition 中的各种方法,可能是历史原因的问题,BeanDefinition 的接口并不是很Spring。主要是 BeanDefinition 中包含了 set 方法和 get 方法,按照 Spring 风格来重新设计的话,可能会拆成两个接口,一个只有 get 方法的 BeanDefinition,和一个能设置属性的 ConfigurableBeanDefinition,类似 BeanFactory 的设计那样,但这都是我的个人理解,读者欢迎来进行讨论。好了,言归正传,我们下面看 BeanDefintion 接口的各种方法,我把对应的 set 和 get 方法放到一起,便于大家理解。

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

  // 1.读写BeanDefinition的父亲
  // BeanDefinition的设计是支持层级的,可以将公共的属性或配置放置到父BeanDefinition中,然后具体实例化时,子BeanDefinition合并下父的属性配置
  // 这部分BeanDefiniton的合并内容会在后续的文件中详细阐述
	void setParentName(@Nullable String parentName);
	String getParentName();


  //2. 读写当前BeanDefinition的Class,这里有一点需要注意的是,这个Class不一定是Bean实际的类型!
  // 如果是一个FactoryBean的话,这里的Class,实际是FactoryBean的类型,具体Bean的类型是由FactoryBean具体的返回值决定的
	void setBeanClassName(@Nullable String beanClassName);
	String getBeanClassName();


 // 3. 读写Bean的范围(我个人习惯翻译为作用域)
 // 这个是用于表示Bean的生命周期的,比如Spring内置了两种作用域 singleton和prototype (单例和原型)
 // 单例的bean在整个Spring容器中只会有一个实例
 // 原型的bean则会根据方法的调用每次都返回一个新的实例
 //
 // 此外 Spring MVC还引入了 Request作用域、Session作用域、和Servlet上下文作用域,
 // 分别用于在 一次请求、一个会话、当前web应用中生命周期的bean
	void setScope(@Nullable String scope);
	String getScope();

  //4. 读写Bean是否是延迟实例化的
  // 如果是延迟的,Spring在容器刷新时,就不会实例化该Bean,而是等到被使用时才会被真正创建
	void setLazyInit(boolean lazyInit);
	boolean isLazyInit();


  //5. 读写Bean依赖的其他Bean
  // 用于表示Bean的依赖层次,必须在先实例化依赖的那些Bean,然后才能实例化当前的Bean
	void setDependsOn(@Nullable String... dependsOn);
	String[] getDependsOn();


 //6. 读写当前Bean是否允许注入到其他Bean中
	void setAutowireCandidate(boolean autowireCandidate);
	boolean isAutowireCandidate();


  //7. 读写当前Bean是否是首要Bean
  // 如果有多个同类型Bean时,首要的Bean则会被注入到其他Bean中
  // 如果有多个同类型Bean,并且没有设置Primary时,则会注入失败
	void setPrimary(boolean primary);
	boolean isPrimary();

  //8 读写创建当前Bean的工厂Bean名字
  // 使用实例工厂方法来创建Bean时,这个名字就表示容器中某个bean,需要结合下面的FactoryMethodName,反射调用后产生当前的Bean
  // 使用静态工厂方法来创建Bean,此值不需要,创建当前Bean只需要BeanClassName和下面的FactoryMethodName
	void setFactoryBeanName(@Nullable String factoryBeanName);
	String getFactoryBeanName();


  //9 读写创建当前Bean的工厂bean中的某个方法名字
	void setFactoryMethodName(@Nullable String factoryMethodName);
	String getFactoryMethodName();


  //10. 读写创建当前Bean的参数列表
  // 需要注意的是,如果是常规的通过构造函数实例化时,此值就是构造函数参数列表
  //如果使用工厂Bean方法(实例工厂和静态工厂)来实例化Bean时,此值时工厂Bean方法的参数列表
	ConstructorArgumentValues getConstructorArgumentValues();
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

  //11. 读写当前Bean的各个属性值
  // Bean的属性值会在调用Bean的初始化方法之前进行设置,MutablePropertyValues则是用于存储当前Bean的各项属性值
	MutablePropertyValues getPropertyValues();
	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

  //12. 读写当前Bean的初始化方法
  // 注意这里的初始化方法是Bean的自定义初始化方法,不是InitializingBean中的接口方法
	void setInitMethodName(@Nullable String initMethodName);
	String getInitMethodName();

  //13. 读写当前Bean的销毁方法
  // 和上面的初始化方法类型,此处是自定义的销毁方法,不是DisposableBean中的方法
	void setDestroyMethodName(@Nullable String destroyMethodName);
	String getDestroyMethodName();

  //14. 读写当前Bean的角色
  // Spring中内置了一些Bean角色
  // ROLE_APPLICATION 应用类型,一般是用户定义的Bean
  // ROLE_SUPPORT     支持类型 用于支持Spring内部的各种功能的Bean
  // ROLE_INFRASTRUCTURE  基础架构Bean 支撑Spring核心机制的Bean,Spring内部使用的,与具体业务无关
	void setRole(int role);
	int getRole();

  //15. 读写当前BeanDefinitoin的描述信息
	void setDescription(@Nullable String description);
	String getDescription();


	// 只读属性


  //1. 返回Spring的一个可解析类型
  // 此类用于表示一个Java的类,可以便捷的解析出类中各种范型参数
	ResolvableType getResolvableType();


  //2. 是否是单例
	boolean isSingleton();


  //3. 是否是原型
	boolean isPrototype();

  //4 是否是抽象的
  // 如果是抽象的,则此BeanDefinition不能够实例化出Bean,只能用于作为其他BeanDefinitoin的父亲
	boolean isAbstract();


  //5 读取BeanDefinition来源的描述信息
	String getResourceDescription();


  //6 获取原始BeanDefinition
  // 这里非常有意思,Spring创建代理Bean时,实际是创建了一个BeanDefinition,顶替了原来的BeanDefintion
  // 原来的BeanDefinition的名字被改成了 $NAME+.target ,并且被设置到新BeanDefinition的OriginatingBeanDefinition属性上。
  //
  // 此方法就是获取被代理的BeanDefinition,替换的前后示意图如下:
  //
  // 1. A名字对应了BeanDefinitionA
  // [
  //    A --> BeanDefinitionA{
  //              OriginatingBeanDefinition: null
  //          }
  // ]
  // 2. 我们要代理A,则使用一个代理 BeanDefintionAProxy, 并用顶替掉原始A,原来的BeanDefinitionA则改名字为 A.target
  // [
  //    A.target --> BeanDefinitionA {
  //                    OriginatingBeanDefinition: null
  //                  }
  //
  //    A      --> BeanDefinitionAProxy {
  //                    OriginatingBeanDefinition: BeanDefinitionA
  //               }
  // ]
  //
	BeanDefinition getOriginatingBeanDefinition();

}

BeanDefinition 的实现

BeanDefinition 的实现与继承结构如下

BeanDefinition.png

AbstractBeanDefinition

BeanDefinition 的抽象类实现,作为其他 BeanDefinitiion 的父类。

  • 注意此 AbstractBeanDefinition 并没有提供 parent 的读写方法实现,即没有提供 parent 字段。这也是下文中为什么会产生了 RootBeanDefinition 和 GenericBeanDefinition
  • 另外,相对于 BeanDefinition 接口,提供了一些更常用的方法

RootBeanDefinition

RootBeanDefinition 的设定是顶级 BeanDefinition,他的 getParent 方法直接返回 null,并且 setParent 不允许调用(调用就会发出异常) 在实例化 Bean 时,各种 BeanDefinition 需要和自己的全部 Parent 进行配置合并,合并后的结果就是一个 RootBeanDefinition,然后利用此 RootBeanDefinition 进行 Bean 的创建。

GenericBeanDefinition

GenericBeanDefinition 是一个通用类型的 BeanDefinition。用户自定义的 BeanDefinition 一般都是此类型,可以指定构造函数参数、属性参数和自己的父 BeanDefinition(提供了 parent 字段)。

GenericBeanDefinition 还有两个继承者 ScannedGenericBeanDefinition 和 ConfigurationPropertiesValueObjectBeanDefinition

ScannedGenericBeanDefinition ScannedGenericBeanDefinition 是用来表示注解扫描出来候选者的 BeanDefinition,使用了 metadata 来存储该类的注解信息。 此类和下文的 AnnotatedGenericBeanDefinition 一样,都是实现了 AnnotatedBeanDefinition 接口,同时继承自 GenericBeanDefinition。

  • 值得注意的是,ScannedGenericBeanDefinition 中的注解不是通过 Java 的反射机制获取的,而是利用了基于 ASM 字节码技术的 MetadataReader 来获取的,所以不会导致 Bean 的类被加载。(关于 Spring 是如何使用 MetadataReader 来读取字节码中的注解信息和类信息的,我后面会专门写文章来讲解,这部分我还是比较熟悉的 😄)

ConfigurationPropertiesValueObjectBeanDefinition ConfigurationPropertiesValueObjectBeanDefinition 并不是一个 public 类,他是一个 package 修饰的类,外部用户一般无法访问他。 此类专门用于表示@ConfigurationProperties注解修饰的类,用于我们业务中的各项属性存储。

ChildBeanDefinition (不重要

ChildBeanDefinition 是一个必须拥有 parent 的 BeanDefinition,如果没有就会抛出异常,此类早期(spring 2.5 之前)有用,目前可以忽略,基本上被 GenericBeanDefinition 替代了。

AnnotatedBeanDefinitionAnnotatedGenericBeanDefinition AnnotatedBeanDefinition 接口是 Spring2.5 版本引入的,支持注解方式的 BeanDefinition,他的实现就是 AnnotatedGenericBeanDefinition 和上文的 ScannedGenericBeanDefinition。

public interface AnnotatedBeanDefinition extends BeanDefinition {

  //1 读取注解数据
	AnnotationMetadata getMetadata();

	@Nullable
  //2 读取该定义的工厂方法的数据(如有
	MethodMetadata getFactoryMethodMetadata();

}

AnnotatedGenericBeanDefinition 同时继承了 GenericBeanDefinition,这样具有注解能力的 BeanDefinition 就产生了。上文中的 ScannedGenericBeanDefinition 和此类的不同点在于:

  • ScannedGenericBeanDefinition 的注解信息来自 ASM 字节码解析
  • AnnotatedGenericBeanDefinition 的注解信息来自于类的反射