zoukankan      html  css  js  c++  java
  • Spring 的IoC容器中核心定义之------BeanDefinition深入分析

    Spring 的IoC容器中核心定义之------BeanDefinition深入分析

    image-20200921161139509

    Spring IoC 容器 Management 一个或多个 bean。这些 bean 是使用您提供给容器的配置元数据创建的(例如,以 XML <bean/>定义的形式)。

    在容器本身内,这些 bean 定义表示为BeanDefinition对象,其中包含(除其他信息外)以下元数据:

    • 包限定的类名:通常,定义了 Bean 的实际实现类。
    • Bean 行为配置元素,用于声明 Bean 在容器中的行为(作用域,生命周期回调等)。
    • 引用其他 bean 进行其工作所需的 bean。这些引用也称为协作者或依赖项。
    • 要在新创建的对象中设置的其他配置设置,例如,池的大小限制或在 Management 连接池的 bean 中使用的连接数。

    该元数据转换为构成每个 bean 定义的一组属性。下表描述了这些属性:

    表 1. bean 定义

    Property Explained in…
    Class Instantiating Beans
    Name Naming Beans
    Scope Bean Scopes
    Constructor arguments Dependency Injection
    Properties Dependency Injection
    Autowiring mode Autowiring Collaborators
    延迟初始化模式 Lazy-initialized Beans
    Initialization method Initialization Callbacks
    Destruction method Destruction Callbacks

    除了包含有关如何创建特定 bean 的信息的 bean 定义之外,ApplicationContext实现还允许注册在容器外部(由用户)创建的现有对象。这是通过getBeanFactory()方法访问 ApplicationContext 的 BeanFactory 来完成的,该方法返回 BeanFactory DefaultListableBeanFactory的实现。 DefaultListableBeanFactory通过registerSingleton(..)registerBeanDefinition(..)方法支持此注册。但是,典型的应用程序只能与通过常规 bean 定义元数据定义的 bean 一起使用。

    BeanDefinition 是容器对于bean配置的内部表示,Spring 将各个 bean 的 BeanDefinition 实例注册记录在 BeanDefinitionRegistry 中,该接口定义了对 BeanDefinition 的各种增删查操作,类似于内存数据库,其实现类 SimpleBeanDefinitionRegistry 主要以 Map 作为存储标的。

    下面转载:https://cloud.tencent.com/developer/article/1497805

    前言

    在前面分析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));

    现在可以开始做菜了吗?其实还不行,因为还没有菜谱(不知道做什么菜,怎么做~) BeanDefinitionRegistryBeanFactory就是这份菜谱,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 图:

    image-20200921162411586

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

    AttributeAccessor:定义了对对象元数据访问的抽象接口

    // 接口都比较简单  就是定义了对对象属性的一些访问方法
    //说明它可以持有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中使用较多,因为它会被代理

    //接口提供了一个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的元数据

    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

    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

    实现代码非常的多:

    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的某些默认设置(例如默认为单例等)。

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

    GenericBeanDefinition:标准bean definition,通用的

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

        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表示。下面看看源码:

    //简单的说:在多继承体系中,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的一些注解信息。

    // 实现了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一个,它就不在容器里了,那就脱离管理了)

    使用案例:

        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
        }
    

    源码参考;

    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类(非常的重要)

    // 它直接继承自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看看即可

    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:

        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。
    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个具体实现

    image-20200921162658227

    AbstractBeanDefinitionReader

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

    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定义信息
    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)

  • 相关阅读:
    asp.net生命周期
    中国互联网公司数据库访问现状
    console在文件中
    2011程序员薪资调查报告全文发布
    Centos上搭建能用于ok6410开发板的tftp服务器
    Centos 上搭建nfs且可挂载到6410开发板
    linux下软件的卸载与安装
    基于ok6410的韦东山驱动视频简要分析lcd驱动
    6410上移植uboot
    编译可加载触摸屏驱动的uImage内核。
  • 原文地址:https://www.cnblogs.com/dalianpai/p/13706388.html
Copyright © 2011-2022 走看看