前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【小家Spring】Spring IoC容器中核心定义之------BeanDefinition深入分析(RootBeanDefinition、ChildBeanDefinition...)

【小家Spring】Spring IoC容器中核心定义之------BeanDefinition深入分析(RootBeanDefinition、ChildBeanDefinition...)

作者头像
YourBatman
发布2019-09-03 16:35:51
7.1K1
发布2019-09-03 16:35:51
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦

前言

在前面分析Spring IoC容器的时候,贯穿全文的一个概念:Bean定义信息。它是Spring容器的一个核心概念,那么本文就深入分析一下BeanDefinition这个接口(类)。

Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition结构

不管是是通过xml配置文件的\<Bean>标签,还是通过注解配置的@Bean,它最终都会被解析成一个Bean定义信息(对象),最后我们的Bean工厂就会根据这份Bean的定义信息,对bean进行实例化、初始化等等操作

从上可知BeanDefinition这个接口对Spring IoC容器的重要之处,所以了解好了它(以及子类),能让我们更大视野的来看Spring管理Bean的一个过程,也能透过现象看本质。

透彻理解Spring容器是打开Spring Boot大门的一把钥匙

下面用一个非常形象的比喻,来形容Spring IoC容器和BeanDefinition之前的关系。

比喻:BeanFactory和BeanDefinition

Spring IoC容器比作一间餐馆,当你来到餐馆,通常会直接招呼服务员:点菜!至于菜的原料是什么?如何用原料把菜做出来?可能你根本就不关心。

IoC容器也是一样,你只需要告诉它需要某个bean,它就把对应的实例(instance)扔给你,至于这个bean是否依赖其他组件,怎样完成它的初始化,根本就不需要你关心

那么问题来了,作为餐馆,想要做出菜肴,得知道菜的原料和菜谱。同样地,IoC容器想要管理各个业务对象以及它们之间的依赖关系,需要通过某种途径来记录和管理这些信息。 BeanDefinition**对象就承担了这个责任**

容器中的每一个bean都会有一个对应的BeanDefinition实例,该实例负责保存bean对象的 所有 必要信息,包括bean对象的class类型、是否是抽象类、构造方法和参数、其它属性等等(所以BeanDefinition就好比做菜的原料)

需要说明的一点是:加入你是自己直接通过 SingletonBeanRegistry#registerSingleton向容器手动注入Bean的,那么就不会存在这份Bean定义信息的,这点需要注意。 Spring内部有不少这样的例子(因为这种Bean非常简单,根本不需要定义信息): beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator); bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext); bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME, Collections.unmodifiableMap(attributeMap));

现在可以开始做菜了吗?其实还不行,因为还没有菜谱(不知道做什么菜,怎么做~)

BeanDefinitionRegistry**和** BeanFactory**就是这份菜谱,**BeanDefinitionRegistry**抽象出bean的注册逻辑,而**BeanFactory**则抽象出了bean的管理逻辑**

各个BeanFactory的实现类就具体承担了bean的注册以及管理工作

DefaultListableBeanFactory作为一个比较通用的BeanFactory实现,它同时也实现了BeanDefinitionRegistry接口,因此它就承担了Bean的注册管理工作

最后我们总结一下比喻关系:
  1. Spring IoC容器:餐馆(服务员)
  2. BeanDefinitionRegistry和 BeanFactory:菜谱
  3. BeanDefinitionRegistry:抽象出来的,向菜谱里注册菜(的管理器)
  4. BeanFactory:抽象出来的,管理这些菜谱(的管理器)
  5. BeanDefinition:原料(做菜所需要的原料)
  6. DefaultListableBeanFactory:具体实施者(具体注册菜谱、做菜的实施者)
  7. 依赖注入的使用者:客户(进店吃饭的人)

BeanDefinition源码分析

总体的 Java Class Diagrams 图:

因为它继承了AttributeAccessor,和BeanMetadataElement,所以我们先有必要来了解下这两个接口:

AttributeAccessor:定义了对对象元数据访问的抽象接口
代码语言:javascript
复制
// 接口都比较简单  就是定义了对对象属性的一些访问方法
//说明它可以持有Bean元数据元素,作用是可以持有XML文件的一个bean标签对应的Object(或者@Configuration元配置对象)
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实现了所有的接口,供其他子类继承使用 主要针对属性CRUD操作

BeanMetadataElement:具有访问source(配置源)的能力

这个方法在@Configuration中使用较多,因为它会被代理

代码语言:javascript
复制
//接口提供了一个getResource()方法,用来传输一个可配置的源对象。
public interface BeanMetadataElement {

	/**
	 * Return the configuration source {@code Object} for this metadata element
	 * (may be {@code null}).
	 * 返回元数据元素配置元对象
	 */
	@Nullable
	Object getSource();
}
BeanDefinition:定义了Bean的各种信息

一个BeanDefinition描述了一个bean的实例,包括属性值,构造方法参数值和继承自它的类的更多信息。

BeanDefinition仅仅是一个最简单的接口,主要功能是允许BeanFactoryPostProcessor

例如PropertyPlaceHolderConfigure 能够检索并修改属性值和别的bean的元数据

代码语言:javascript
复制
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	// 单例Bean还是原型Bean
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
	
	// Bean角色
	int ROLE_APPLICATION = 0; //应用程序重要组成部分
	int ROLE_SUPPORT = 1; //做为大量配置的一部分(支持、扩展类)  实际上就是说,我这个Bean是用户的,是从配置文件中过来的。
	int ROLE_INFRASTRUCTURE = 2; //指内部工作的基础构造  实际上是说我这Bean是Spring自己的,和你用户没有一毛钱关系


	// Modifiable attributes
	//parent definition(若存在父类的话,就设置进去)
	void setParentName(@Nullable String parentName);
	@Nullable
	String getParentName();
	
	// 指定Class类型。需要注意的是该类型还有可能被改变在Bean post-processing阶段
	// 若是getFactoryBeanName  getFactoryMethodName这种情况下会改变
	void setBeanClassName(@Nullable String beanClassName);
	@Nullable
	String getBeanClassName();

	//SCOPE_SINGLETON或者SCOPE_PROTOTYPE两种
	void setScope(@Nullable String scope);
	@Nullable
	String getScope();

	// @Lazy 是否需要懒加载(默认都是立马加载的)
	void setLazyInit(boolean lazyInit);
	boolean isLazyInit();
	
	// 此Bean定义需要依赖的Bean(显然可以有多个)
	void setDependsOn(@Nullable String... dependsOn);
	@Nullable
	String[] getDependsOn();
	
	// 这个Bean是否允许被自动注入到别的地方去(默认都是被允许的)
	// 注意:此标志只影响按类型装配,不影响byName的注入方式的~~~~
	void setAutowireCandidate(boolean autowireCandidate);
	boolean isAutowireCandidate();

	// 是否是首选的  @Primary
	void setPrimary(boolean primary);
	boolean isPrimary();
	
	// 指定使用的工厂Bean(若存在)的名称~
	void setFactoryBeanName(@Nullable String factoryBeanName);
	@Nullable
	String getFactoryBeanName();
	//指定工厂方法~
	void setFactoryMethodName(@Nullable String factoryMethodName);
	@Nullable
	String getFactoryMethodName();
	
	// 获取此Bean的构造函数参数值们  ConstructorArgumentValues:持有构造函数们的 
	// 绝大多数情况下是空对象 new ConstructorArgumentValues出来的一个对象
	// 当我们Scan实例化Bean的时候,可能用到它的非空构造,这里就会有对应的值了,然后后面就会再依赖注入了
	ConstructorArgumentValues getConstructorArgumentValues();
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	// 获取普通属性集合~~~~
	MutablePropertyValues getPropertyValues();
	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	// Read-only attributes
	boolean isSingleton();
	boolean isPrototype();
	boolean isAbstract();
	
	// 对应上面的role的值
	int getRole();
	//@Description
	@Nullable
	String getDescription();
	// 返回该Bean定义来自于的资源的描述(用于在出现错误时显示上下文)
	@Nullable
	String getResourceDescription();
	//返回原始BeanDefinition,如果没有则返回@null
	// 若这个Bean定义被代理、修饰过  这个方法可以返回原始的
	@Nullable
	BeanDefinition getOriginatingBeanDefinition();
}

抽象实现、实现类们。上面已经画出了一些类的结构图,下面一个个来看

子接口:AnnotatedBeanDefinition
代码语言:javascript
复制
public interface AnnotatedBeanDefinition extends BeanDefinition {
	//获取该bean definition的注解元数据
	AnnotationMetadata getMetadata();
	
	//@since 4.1.1
	//Obtain metadata for this bean definition's factory method(如果不存在就返回null)
	@Nullable
	MethodMetadata getFactoryMethodMetadata();
}

AnnotationMetadata定义了访问特定类的注解的抽象接口,它不需要加载该类即可访问

该注解Bean定义旗下三大实现类:ScannedGenericBeanDefinitionConfigurationClassBeanDefinitionAnnotatedGenericBeanDefinition

抽象实现:AbstractBeanDefinition

AbstractBeanDefinition实现了BeanDefinition定义的一系列操作,定义了描述Bean画像的一系列属性,在AbstractBeanDefinition的基础上,Spring衍生出了一系列具有特殊用途的BeanDefinition

实现代码非常的多:

代码语言:javascript
复制
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
		
	//=====================定义众多常量。这一些常量会直接影响到spring实例化Bean时的策略
	// 个人觉得这些常量的定义不是必须的,在代码里判断即可。Spring定义这些常量的原因很简单,便于维护,让读代码的人知道每个值的意义(所以以后我们在书写代码时,也可以这么来搞)

	//默认的SCOPE,默认是单例
	public static final String SCOPE_DEFAULT = "";
	
	// 自动装配的一些常量
	public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
	public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
	public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
	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的销毁方法的名字,目前来说,推断的销毁方法的名字一般为close或是shutdown
	//(即未指定Bean的销毁方法,但是内部定义了名为close或是shutdown的方法,则容器推断其为销毁方法)
	public static final String INFER_METHOD = "(inferred)";


	//=====================属性:基本囊括了Bean实例化需要的所有信息
	
	//Bean的class对象或是类的全限定名
	@Nullable
	private volatile Object beanClass;

	//默认的scope是单例
	@Nullable
	private String scope = SCOPE_DEFAULT;
	//默认不为抽象类
	private boolean abstractFlag = false;
	//默认不进行自动装配
	private boolean lazyInit = false;
	//默认不是懒加载
	private int autowireMode = AUTOWIRE_NO;
	//默认不进行依赖检查
	private int dependencyCheck = DEPENDENCY_CHECK_NONE;
	// @@DependsOn 默认没有
	@Nullable
	private String[] dependsOn;
	// autowire-candidate属性设置为false,这样容器在查找自动装配对象时,将不考虑该bean,
	// 备注:并不影响本身注入其它的Bean
	private boolean autowireCandidate = true;
	// 默认不是首选的
	private boolean primary = false;
	
	//用于记录Qualifier,对应子元素qualifier=======这个字段有必要解释一下
	// 唯一向这个字段放值的方法为本类的:public void addQualifier(AutowireCandidateQualifier qualifier)    copyQualifiersFrom这个不算,那属于拷贝
	// 调用处:AnnotatedBeanDefinitionReader#doRegisterBean  但是Spring所有调用处,qualifiers字段传的都是null~~~~~~~~~尴尬
	// 通过我多放跟踪发现,此处这个字段目前【永远】不会被赋值(除非我们手动调用对应方法为其赋值)   但是有可能我才疏学浅,若有知道的  请告知,非常非常感谢  我考虑到它可能是预留字段~~~~
	// 我起初以为这样可以赋值:
	//@Qualifier("aaa")
	//@Service
	//public class HelloServiceImpl   没想到,也是不好使的,Bean定义里面也不会有值
	// 因此对应的方法getQualifier和getQualifiers 目前应该基本上都返回null或者[]
	private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>(0);
	//我理解为通过这个函数的逻辑初始化Bean,而不是构造函数或是工厂方法(相当于自己去实例化,而不是交给Bean工厂)
	@Nullable
	private Supplier<?> instanceSupplier;
	//是否允许访问非public方法和属性,应用于构造函数、工厂方法、init、destroy方法的解析 默认是true,表示啥都可以访问
	private boolean nonPublicAccessAllowed = true;
	// 是否以一种宽松的模式解析构造函数,默认为true(宽松和严格体现在类型匹配上)
	private boolean lenientConstructorResolution = true;
	//工厂类名(注意是String类型,不是Class类型) 对应bean属性factory-method
	@Nullable
	private String factoryBeanName;
	//工厂方法名(注意是String类型,不是Method类型)
	@Nullable
	private String factoryMethodName;
	//记录构造函数注入属性,对应bean属性constructor-arg
	@Nullable
	private ConstructorArgumentValues constructorArgumentValues;
	
	//Bean属性的名称以及对应的值,这里不会存放构造函数相关的参数值,只会存放通过setter注入的依赖
	@Nullable
	private MutablePropertyValues propertyValues;
	//方法重写的持有者,记录lookup-method、replaced-method元素  @Lookup等
	@Nullable
	private MethodOverrides methodOverrides;

	//init函数的名字
	@Nullable
	private String initMethodName;
	//destory函数的名字
	@Nullable
	private String destroyMethodName;
	//是否执行init-method,程序设置
	private boolean enforceInitMethod = true;
	private boolean enforceDestroyMethod = true;

	//是否是合成类(是不是应用自定义的,例如生成AOP代理时,会用到某些辅助类,这些辅助类不是应用自定义的,这个就是合成类)
	//创建AOP时候为true
	private boolean synthetic = false;
	
	//Bean的角色,为用户自定义Bean
	private int role = BeanDefinition.ROLE_APPLICATION;

	// Bean的描述信息
	@Nullable
	private String description;
	//the resource that this bean definition came from
	// 这个Bean哪儿来的
	@Nullable
	private Resource resource;

	//=====================方法:就不逐一解释了,大部分都是get、set 只贴出一些特殊的

	// 其实就是给reource赋值了,使用了BeanDefinitionResource
	public void setOriginatingBeanDefinition(BeanDefinition originatingBd) {
		this.resource = new BeanDefinitionResource(originatingBd);
	}
	// 上面有赋值,所以get的时候就是返回上面set进来的值
	public BeanDefinition getOriginatingBeanDefinition() {
		return (this.resource instanceof BeanDefinitionResource ?
				((BeanDefinitionResource) this.resource).getBeanDefinition() : null);
	}
	
	//克隆Bean的定义信息
	@Override
	public Object clone() {
		return cloneBeanDefinition();
	}
	public abstract AbstractBeanDefinition cloneBeanDefinition();
}

AbstractBeanDefinition定义了一系列描述Bean画像的属性,通过这个类,可以窥见Bean的某些默认设置(例如默认为单例等)。

从上图可以看出,接下俩需要看具体衍生出来的实现类了,先看**RootBeanDefinition**、**ChildBeanDefinition**、**GenericBeanDefinition**。他们都是**AbstractBeanDefinition**的直接实现类

GenericBeanDefinition:标准bean definition,通用的

除了具有指定类、可选的构造参数值和属性参数这些其它bean definition一样的特性外,它还具有通过parenetName属性来灵活(动态)设置parent bean definition,而非硬编码作为root bean definition

代码语言:javascript
复制
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);

        // 向工厂里注册Bean信息
        GenericBeanDefinition parentBeanDef = new GenericBeanDefinition();
        parentBeanDef.setBeanClass(Parent.class);
        parentBeanDef.setBeanClassName(Parent.class.getName());
        parentBeanDef.setScope(BeanDefinition.SCOPE_SINGLETON);

        // 就这样,我们可以动态的给子Bean 设置一个父Bean进去
        GenericBeanDefinition childBeanDef = new GenericBeanDefinition();
        childBeanDef.setParentName(parentBeanDef.getBeanClassName());
        childBeanDef.setBeanClass(Child.class);

        applicationContext.registerBeanDefinition("parent", parentBeanDef);
        applicationContext.registerBeanDefinition("child", childBeanDef);

        System.out.println(applicationContext.getBeanDefinition("parent"));
        System.out.println(applicationContext.getBeanDefinition("child")); //Generic bean with parent 'com.fsx.bean.Parent': class [com.fsx.bean.Child]; scope=;...
    }

GenericBeanDefinition源码实现非常的的简单,只增加了一个parentName的属性值,其余的实现都在父类AbstractBeanDefinition

备注:若你是xml配置,最初被加载进来都是一个GenericBeanDefinition,之后再逐渐解析的。

ChildBeanDefinition:子Bean定义信息,依赖于父类RootBeanDefinition

ChildBeanDefinition是一种bean definition,它可以继承它父类的设置,即ChildBeanDefinitionRootBeanDefinition有一定的依赖关系

(功能和GenericBeanDefinition),所以此处忽略~

从spring 2.5 开始,提供了一个更好的注册bean definition类GenericBeanDefinition,所以以后推荐使用它。

RootBeanDefinition

一个RootBeanDefinition定义表明它是一个可合并的bean definition:即在spring beanFactory运行期间,可以返回一个特定的bean。但在Spring2.5以后,我们绝大多数情况还是可以使用GenericBeanDefinition来做。

我们非常熟悉的**final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);**这句代码,就是去合并parent的属性进来,这样体现了继承的强大。属性也才完整。

在 配置文件中可以定义父和子,父用RootBeanDefinition表示, 而子用ChildBeanDefiniton表示,而没有父的就使用 RootBeanDefinition表示。下面看看源码:

代码语言:javascript
复制
//简单的说:在多继承体系中,RootBeanDefinition代表的是当前初始化类的父类的BeanDefinition 若没有父类,那就是它自己嘛
public class RootBeanDefinition extends AbstractBeanDefinition {
	
	//BeanDefinitionHolder存储有Bean的名称、别名、BeanDefinition
	@Nullable
	private BeanDefinitionHolder decoratedDefinition;
	// AnnotatedElement 是java反射包的接口,通过它可以查看Bean的注解信息
	@Nullable
	private AnnotatedElement qualifiedElement;
	//允许缓存
	boolean allowCaching = true;
	//从字面上理解:工厂方法是否唯一
	boolean isFactoryMethodUnique = false;
	//封装了java.lang.reflect.Type,提供了泛型相关的操作,具体请查看:
	// ResolvableType 可以专题去了解一下子,虽然比较简单 但常见
	@Nullable
	volatile ResolvableType targetType;
	//缓存class,表明RootBeanDefinition存储哪个类的信息
	@Nullable
	volatile Class<?> resolvedTargetType;
	//缓存工厂方法的返回类型
	@Nullable
	volatile ResolvableType factoryMethodReturnType;

	/** Common lock for the four constructor fields below */
	final Object constructorArgumentLock = new Object();

	//缓存已经解析的构造函数或是工厂方法,Executable是Method、Constructor类型的父类
	@Nullable
	Executable resolvedConstructorOrFactoryMethod;
	//表明构造函数参数是否解析完毕
	boolean constructorArgumentsResolved = false;
	//缓存完全解析的构造函数参数
	@Nullable
	Object[] resolvedConstructorArguments;
	//缓存待解析的构造函数参数,即还没有找到对应的实例,可以理解为还没有注入依赖的形参
	@Nullable
	Object[] preparedConstructorArguments;

	/** Common lock for the two post-processing fields below */
	final Object postProcessingLock = new Object();

	//表明是否被MergedBeanDefinitionPostProcessor处理过
	boolean postProcessed = false;
	//在生成代理的时候会使用,表明是否已经生成代理
	@Nullable
	volatile Boolean beforeInstantiationResolved;

	//实际缓存的类型是Constructor、Field、Method类型
	@Nullable
	private Set<Member> externallyManagedConfigMembers;
	//InitializingBean中的init回调函数名——afterPropertiesSet会在这里记录,以便进行生命周期回调
	@Nullable
	private Set<String> externallyManagedInitMethods;
	//DisposableBean的destroy回调函数名——destroy会在这里记录,以便进行生命周期回调
	@Nullable
	private Set<String> externallyManagedDestroyMethods;

	//===========方法(只例举部分)
	// 由此看出,RootBeanDefiniiton是木有父的
	@Override
	public String getParentName() {
		return null;
	}
	@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");
		}
	}

	// 拿到class类型
	@Nullable
	public Class<?> getTargetType() {
		if (this.resolvedTargetType != null) {
			return this.resolvedTargetType;
		}
		ResolvableType targetType = this.targetType;
		return (targetType != null ? targetType.resolve() : null);
	}

	@Override
	public RootBeanDefinition cloneBeanDefinition() {
		return new RootBeanDefinition(this);
	}
}

可以看到许多与反射相关的对象,这说明spring底层采用的是反射机制

总结一下,RootBeanDefiniiton保存了以下信息:

  1. 定义了id、别名与Bean的对应关系(BeanDefinitionHolder)
  2. Bean的注解(AnnotatedElement)
  3. 具体的工厂方法(Class类型),包括工厂方法的返回类型,工厂方法的Method对象
  4. 构造函数、构造函数形参类型
  5. Bean的class对象

可以看到,RootBeanDefinition与AbstractBeanDefinition是互补关系,RootBeanDefinition在AbstractBeanDefinition的基础上定义了更多属性,初始化Bean需要的信息基本完善


接下来,再在看看AnnotatedBeanDefinition的三个子类:

ScannedGenericBeanDefinition:存储@Component、@Service、@Controller等注解注释的类

它的源码很简单,就是多了一个属性:private final AnnotationMetadata metadata用来存储扫描进来的Bean的一些注解信息。

代码语言:javascript
复制
// 实现了AnnotatedBeanDefinition 也继承了GenericBeanDefinition
public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
	private final AnnotationMetadata metadata;
	...
	// 它只有一个构造函数:必须传入MetadataReader
	public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
		Assert.notNull(metadataReader, "MetadataReader must not be null");
		this.metadata = metadataReader.getAnnotationMetadata();
		setBeanClassName(this.metadata.getClassName());
	}
}
AnnotatedGenericBeanDefinition

在基于注解驱动的Spring应用着,它使用得非常的多。因为获取注解信息非常的方便~

AnnotatedGenericBeanDefinition只能用于已经被注册或被扫描到的类(否则你手动new一个,它就不在容器里了,那就脱离管理了)

使用案例:

代码语言:javascript
复制
    public static void main(String[] args) {
        AnnotatedBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(RootConfig.class);
        // 就这么一下子,就把注解们都拿到了,简直不要太方便,简直可以当工具类来用
        Set<String> annotationTypes = beanDefinition.getMetadata().getAnnotationTypes();
        System.out.println(annotationTypes); //[org.springframework.context.annotation.ComponentScan, org.springframework.context.annotation.Configuration]
        System.out.println(beanDefinition.isSingleton()); //true
        System.out.println(beanDefinition.getBeanClassName()); //com.config.RootConfig
    }

源码参考;

代码语言:javascript
复制
public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

	private final AnnotationMetadata metadata;
	@Nullable
	private MethodMetadata factoryMethodMetadata;

	/**
	 * Create a new AnnotatedGenericBeanDefinition for the given bean class.
	 * @param beanClass the loaded bean class  注意官方这个注释:已经加载进来了的Bean的Class
	 */
	public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
		setBeanClass(beanClass);
		this.metadata = new StandardAnnotationMetadata(beanClass, true);
	}

	//@since 3.1.1 此处传入AnnotationMetadata ,也得保证对应的class已经被loaded
	public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata) {
		Assert.notNull(metadata, "AnnotationMetadata must not be null");
		if (metadata instanceof StandardAnnotationMetadata) {
			setBeanClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
		}
		else {
			setBeanClassName(metadata.getClassName());
		}
		this.metadata = metadata;
	}

	 //@since 4.1.1   可以由指定的工厂方法产生这个Bean
	public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata, MethodMetadata factoryMethodMetadata) {
		this(metadata);
		Assert.notNull(factoryMethodMetadata, "MethodMetadata must not be null");
		setFactoryMethodName(factoryMethodMetadata.getMethodName());
		this.factoryMethodMetadata = factoryMethodMetadata;
	}

	@Override
	public final AnnotationMetadata getMetadata() {
		 return this.metadata;
	}
	@Override
	@Nullable
	public final MethodMetadata getFactoryMethodMetadata() {
		return this.factoryMethodMetadata;
	}

}
ConfigurationClassBeanDefinition

首先需要注意的是,它是ConfigurationClassBeanDefinitionReader的一个私有的静态内部类:这个类负责将@Bean注解的方法转换为对应的ConfigurationClassBeanDefinition类(非常的重要)

代码语言:javascript
复制
// 它直接继承自RootBeanDefinition 
private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
	... 源码和之前的差得不是太多,此处就不解释了
}

它有一些默认的设置处理如下:

  • 如果@Bean注解没有指定bean的名字,默认会用方法的名字命名bean
  • @Configuration注解的类会成为一个工厂类,而所有的@Bean注解的方法会成为工厂方法,通过工厂方法实例化Bean,而不是直接通过构造函数初始化(所以我们方法体里面可以很方便的书写逻辑。。。)

Spring初始化时,会用GenericBeanDefinition或是ConfigurationClassBeanDefinition用@Bean注解注释的类)存储用户自定义的Bean,在初始化Bean时,又会将其转换为RootBeanDefinition

BeanDefinitionBuilder:快速创建一个Bean定义

使用它的好处是,可以进行方法的连缀。

没有特殊指明,创建的都是GenericBeanDefinition,源码非常的简单,下面只用个Deme看看即可

代码语言:javascript
复制
public class BeanDefinitionBuilder {
	//=================创建一个Builder  没特殊指明,都是GenericBeanDefinition
	public static BeanDefinitionBuilder genericBeanDefinition() {
		return new BeanDefinitionBuilder(new GenericBeanDefinition());
	}
	....
	public static BeanDefinitionBuilder rootBeanDefinition(String beanClassName) {
		return rootBeanDefinition(beanClassName, null);
	}
	public static BeanDefinitionBuilder childBeanDefinition(String parentName) {
		return new BeanDefinitionBuilder(new ChildBeanDefinition(parentName));
	}
}

demo:

代码语言:javascript
复制
    public static void main(String[] args) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Child.class)
                .setRole(BeanDefinition.ROLE_APPLICATION)
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .addPropertyValue("name", "fsx")
                .setLazyInit(false)
                //Spring5.0后提供的,可以自己书写函数,在里面做任意事情
                //bdf是个AbstractBeanDefinition
                .applyCustomizers((bdf) -> {
                    AbstractBeanDefinition abdf = (AbstractBeanDefinition) bdf;
                    abdf.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_NO);
                }).getRawBeanDefinition();
        System.out.println(beanDefinition); //Generic bean: class [com.fsx.maintest.Child]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; d...
    }

BeanDefinitionReader:该接口的作用就是加载 Bean

在 Spring 中,Bean 一般来说都在配置文件中定义。而在配置的路径由在 web.xml 中定义(还有全注解的方式)。所以加载 Bean 的步骤大致就是:

  1. 加载资源,通过配置文件的路径(Location)加载配置文件(Resource)
  2. 解析资源,通过解析配置文件的内容得到 Bean。
代码语言:javascript
复制
public interface BeanDefinitionReader {

	// 得到Bean定义的register 
	BeanDefinitionRegistry getRegistry();
	// 返回用于加载资源的 ResourceLoader(可以为null)
	@Nullable
	ResourceLoader getResourceLoader();
	// 加载Bean的类加载器
	@Nullable
	ClassLoader getBeanClassLoader();
	// 生成Bean名称的名字生成器(若没有指定名称的话,会调用它生成)
	BeanNameGenerator getBeanNameGenerator();


	// 核心方法,loadbean定义进来,然后注册到上面的register 里面去
	int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
	int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
	int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
	int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;

}

它的继承结构非常简单,一个抽象实现+3个具体实现

AbstractBeanDefinitionReader

它实现了一些基本的方法,但是核心方法loadBeanDefinitions肯定是交给子类实现了

代码语言:javascript
复制
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
	private final BeanDefinitionRegistry registry;
	@Nullable
	private ResourceLoader resourceLoader;
	@Nullable
	private ClassLoader beanClassLoader;
	// 会有环境变量
	private Environment environment;
	// 默认的名字生成器(类名首字母小写)
	private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();

	// 此构造函数,会完成一些参数的初始化
	protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;

		// Determine ResourceLoader to use.
		if (this.registry instanceof ResourceLoader) {
			this.resourceLoader = (ResourceLoader) this.registry;
		} else {
			// 注意这个处理~~~~
			this.resourceLoader = new PathMatchingResourcePatternResolver();
		}

		// Inherit Environment if possible
		// 如果注册器里有环境变量,就用它的 否则new一个标准的~~~~  它下面也提供了set方法可以设置
		if (this.registry instanceof EnvironmentCapable) {
			this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
		} else {
			this.environment = new StandardEnvironment();
		}
	}

	public void setEnvironment(Environment environment) {
		Assert.notNull(environment, "Environment must not be null");
		this.environment = environment;
	}
	public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
		this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new DefaultBeanNameGenerator());
	}

	...	
}
XmlBeanDefinitionReader:从xml中加载Bean定义信息
代码语言:javascript
复制
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	...
}

我们注解配置中@Configuration上也可以加上@ImportResource导入外置的xml配置文件。它由此方法ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromImportedResources处理,内部借助的就是XmlBeanDefinitionReader去解析它的

PropertiesBeanDefinitionReader:直接从properties文件或者Map里加载Bean

由于它语法怪异,因此基本不适用了

GroovyBeanDefinitionReader:不在本文讨论中

可能有小伙伴问:那我们注解的@Bean以及@Component的这么些bean定义都是谁去加载的呢? 需要注意的是这个就不属于它了。 @Bean都是@Configuration配置类里,统一由ConfigurationClassParser#parse()里去处理的(直接执行Method就行) @Component这种组件统一由解析@ComponentScan的处理器的ComponentScanAnnotationParser(借助ClassPathBeanDefinitionScanner) 参考博文:【小家Spring】Spring解析@ComponentScan注解源码分析(ComponentScanAnnotationParser、ClassPathBeanDefinitionScanner)

总结

本编文章旨在讲解贯穿Spring IoC容器上下文的Bean定义接口、实现类等等。从设计中我们能发现,Spring的设计原则还是非常优秀的,单一职责的特性。

宁愿用扩展的方法多写类,也不会在Base里面加内容变得臃肿最终几乎笨重不可维护

有了这些基础,相信你在看Spring源码的时候又能更加顺畅很多了~共勉

(RootBeanDefinition、AnnotatedGenericBeanDefinition)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年03月23日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 比喻:BeanFactory和BeanDefinition
  • BeanDefinition源码分析
    • AttributeAccessor:定义了对对象元数据访问的抽象接口
      • BeanMetadataElement:具有访问source(配置源)的能力
        • BeanDefinition:定义了Bean的各种信息
          • 子接口:AnnotatedBeanDefinition
            • 抽象实现:AbstractBeanDefinition
              • ChildBeanDefinition:子Bean定义信息,依赖于父类RootBeanDefinition
                • RootBeanDefinition:
                  • ScannedGenericBeanDefinition:存储@Component、@Service、@Controller等注解注释的类
                    • AnnotatedGenericBeanDefinition
                      • ConfigurationClassBeanDefinition
                        • BeanDefinitionBuilder:快速创建一个Bean定义
                        • BeanDefinitionReader:该接口的作用就是加载 Bean
                          • 总结
                          相关产品与服务
                          容器服务
                          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档