经过前面的分析,终于结束了对XML配置文件的解析, 接下来就是对bean加载的探索,bean加载功能的实现远比bean的解析复杂的多,对于加载bean的功能,在Spring中的调用方法为:
public Object getBean(String name) throws BeanException
return doGetBean(name,null,null,false)protected < T> T doGetBean(final String beanName,final Class< T> requireType,final Object [] args,boolean typeCheckOnly) throws BeanException
// 提取对应的beanName
// 检查缓存中或者实例工厂中是否有对应的实例,为什么首先使用这段代码呢,因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring创建bean的原则是不等bean创建完成就会创建bean的ObjectFactory提早曝光,也就是将ObjectFactory加入到缓存中,一旦下个bean创建的时候需要依赖上一个bean则直接使用ObjectFactory
// 直接尝试从缓存或singletonFactories中的ObjectFactory中获取
// 返回对应的示例,有时候存在BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回实例
// 当在单例情况下才会尝试解决循环依赖,原型模式情况下,如果存在A中有B属性,B中有A属性,那么依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖
// 如果beanDefinitionMap中也就是在所有已加载的类中不包括beanName则尝试从parentBeanFactory中检测
// 递归到BeanFactory中寻找
// 如果不是仅仅做类型检查而是创建bean,需要进行记录
// 将储存在GenericBeanDefinition转换为RootBeanDefinition,如果指定BeanName是子Bean的话同时会合并父类的相关属性
// 若存在依赖则需要递归实例化依赖的bean,并缓存依赖调用
// 实例化依赖的bean后便可以实例化mbd本身了
// singleton模式的创建
// prototype模式的创建
// 指定scope上实例化bean
// 检查需要的类型是否符合bean的实际类型
仅从代码量上就能看出bean的加载过程经历了一个相当复杂的过程,其中涉及到各种各样的考虑,对于加载过程中所涉及的步骤大致如下:
- 转换对应的beanName,获取很多人不理解转换对应beanName是什么意思,传入的参数name不就是beanName吗?其实不是,这里传入的参数有可能是别名,也有可能是FactoryBean,所以需要进行一系列的解析,这些解析内容包括如下内容。
1.1 去除FactroyBean的修饰符,也就是如果那么=“&aa”,那么会首先去除&而使name=“aa”。
1.2 去除alisa所表示的最终beanName,例如别名A指向名称为B的bean则返回B;若别名A指向别名B,别名B又指向名称为C的bean则返回C。- 尝试从缓存加载单例,单例在Spring的统一个容器中只会被创建一次,后续在获取bean,就直接从单例缓存中获取了。当然这里也尝试加载,如果不成功则再次尝试从singletonFactories中加载。因为在创建单例bean的时候会在存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean直接使用ObjectFactory
- bean的实例化,如果从缓存中得到了bean的原始状态,则需要对bean进行实例化。这里必须强调一下,缓存中记录的只是最原始的bean状态,并不一定是最终想要的bean。所有使用getObjectForBeanInstance完成这个工作。
- 原型模式的依赖检查,只用在单例情况下才会尝试解决循环依赖,如果存在A中有B的属性,B中有A的属性,那么当依赖注入的时候,就是产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖,也就是清苦啊:isPrototypeCurrentlyInCreation(beanName)判断为true。
- 检测parentBeanFactory,从代码上看,如果缓存没有数据的话直接转到父类工厂上去加载了,这是为什么呢?它是检测如果加载的XML配置文件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试加载了,然后再去递归的调用getBean方法。
- 将储存XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,因为从XML配置文件读取到的Bean的信息是存储在GenericBeanDefinition中的,但是所有的Bean后续处理都是针对RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则会一并合并父类的属性。
- 寻找依赖,因为bean的初始化过程中可能会用到某些属性,而某些属性可能是动态配置且配置成依赖于其他的bean,那么这个时候就有必要先加载依赖的bean,所以,在Spring加载顺序中在初始化某一个bean的时候首先会初始化这个bean所对应的依赖。
- 针对不同的scope进行bean的创建,在Spring中存在不同的scope,其中默认的是singleton,但是还有注入prototype,request之类的,在这个步骤中,Spring会根据不同的配置进行不同的初始化策略。
- 类型转换,程序到这里返回bean后基本结束了,通常对该方法的调用参数requireType是为空的,但是可能会存在这样的情况,返回的bean其实是一个String,但是requireType却传入Integer类型,那么这时候本步骤就会起作用了,它的功能就是将返回的bean转换为requireType所指定的类型。当然,String转换为Integer是最简单的一种转换,在Spring中提供了各种各样的转换器,用户也可以自己扩展转换器来满足自己的需求。
经过上面的步骤后bean的加载就结束了,这个时候就可以返回所需要的bean了,其中最重要的就是步骤8,针对不同的scope进行bean的创建,各种常用的Spring特性就是在这里的实现。
5.1 FactoryBean的使用
一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean。在某些情况下,实例化bean过程比较复杂,如果按照传统方式,则需要在< bean>中提取大量的配置信息,配置方式的灵活性是受限的,这时候采用编码的方式可能会得到一个简单方案。Spring为此提供了一个org.SpringFramework.bean.factory.FactoryBean工厂类接口,用户可以通过实现该接口定制实现实例化bean的逻辑。
FactoryBean接口对于Spring框架来说占有重要的地位,Spring自身提供了70多种FactoryBean的实现,它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean< T>的形式。
在接口中还定义了以下三个方法:
T getObject:返回有FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例还会放到Spring容器中单实例缓存池中。
boolean isSingleton():返回有FactoryBean创建的bean实例的作用域是singleton还是prototype。
Class< T> getObjectType() :返回FactoryBean创建的bean类型。
当配置文件中< bean>class属性配饰的实现类是FactoryBean时,通过getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()方法代理了getBean()方法。
5.2 缓存获取单例bean
介绍过FactoryBean的用法后,就可以了解bean的加载过程了。前面提到过,单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例缓存中获取,当然这里只是尝试加载,首先尝试从缓存中加载,然后再次尝试从singletonFactories中加载。因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring创建bean的原则是不等bean创建完成就会将bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时需要依赖上一个bean吗,则直接使用ObjectFactory。
protected Object getSingleton(String beanName,boolean allowEarlyReference)
// 检查缓存中是否存在实例
// 如果为空,则锁定全局变量并进行处理
// 如果此bean正在加载则不处理
// 当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories
// 调用预先设定的getObject方法
// 记录在缓存中,earlySingletonObjects和singletonFactories互斥
这个方法首先尝试从singletonObjects里获取实例,如果获取不到再从earlySingletonObjects获取,如果还获取不到,在尝试从singletonFactories里获取beanName对应的objectFactory,然后调用这个ObjectFactory的getObject来创建bean,并放到earlySingletonObjects里面,并且从singletonFactories里面move掉这个ObjectFactory,而后对后续的所有操作都只是为了循环依赖检测的时候使用,也就是在allowEarlyReference为true的情况下才会使用。
这里涉及用于存储bean的不同的map:
singletonObjects:用于保存BeanName和创建bean实例之间的关系,bean name --> bean instance
singletonFactories:用于保存BeanName和创建bean的工厂之间的关系,bean name --> ObjectFactory
earlySingletonObjects:也是保存BeanName和创建bean实例之间的关系,与singletonObjects不同之处是,当一个单例bean被放到这里时,那么当bean还在创建过程中,就可以通过getBean方法获取到了,其目的是用来检测循环引用的。
registeredSingleton:用于保存当前所用已注册的bean。
5.3 从bean的实例中获取对象
在getBean中,getObjectForBeanInstance是个高频率使用的方法,无论是从缓存获得bean还是根据不同的scope策略加载bean。总之,我们得到bean的实例后第一步就是调用这个方法来检验正确性,其实就是用于检测当前bean是否是FactoryBean类型的bean,如果是,那么需要调用该bean对应的FactoryBean实例的getObject()作为返回值。
无论是从缓存中获取的bean还是通过不同的scope策略记载的bean都只是最原始的bean状态,并不一定是最终想要的bean。假设需要工厂bean进行处理,那么这里得到的其实是工厂的bean的初始状态,但是真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjectForBeanInstance方法就是完成这个工作。
protected Object getObjectForBeanInstance(Object beanInstance,String name,String beanName,RootBeanDefinition mbd)
// 如果指定的name是工厂相关(以&为前缀)且beanInstance又不是FactoryBean类型则验证不通过
// 如果有了个bean的实例,这个实例可能会是正常的bean或者是FactoryBean,如果是FactoryBean则使用创建实例,但是如果用户想直接获取工厂实例而不是工厂的getObject方法对应的实例那么传入那么应该加前缀&
// 加载FactoryBean
// 尝试从缓存中加载bean
// 如果已经明确知道beanInstance一定是FactoryBean类型,则containsBeanDefinition检测beanDefinitionMap中也就是在所有已加载的类中检测是否定义beanName
// 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,如果指定BeanName是子Bean的话同时会合并父类相关属性
// 是否是用户自定义的而不是应用程序本身定义的
从代码来看,其实这个方法没有什么重要的信息,大多是辅助代码以及一些功能性的判断,真正核心代码却委托给了getObjectFromFactoryBean,getObjectForBeanInstance中所做的工作:
- 对FactoryBean正确性的校验;
- 对非FactoryBean不做任何处理;
- 对bean进行转换;
- 将从FactoryBean中解析bean的工作委托给getObjectFromFactoryBean。
这个代码中只做了一件事就是返回的bean如果是单例的,那么必须要保证全局唯一,同时,也因为是单例的,所以不需要重复创建,可以使用缓存来提高性能,也就是说已经加载过就要记录下来以便下次复用,否则的话就直接获取了。
在doGetObjectFromFactoryBean方法中终于看到了想要看到的方法,也就是object = factory.getObject()。
private Object doGetObjectFromFactoryBean(final FactoryBean factory,final String beanName,final boolean shouldPostProcess) throws BeanCreateionException
// 需要权限验证
// 直接调用getObject方法
// 调用ObjectFactory的后处理器
上面已经讲述了FactoryBean的调用方法,如果bean声明为FactoryBean类型,则当提取bean时提取的并不是FactoryBean,而是FactoryBean中对应的getObject方法方法返回的bean,doGetObjectFromFactoryBean正式实现了这个功能,但是上面的方法中除了调用object = factory.getObject()得到想要的结果后并没有直接返回,而是接下来又做了后处理的操作,于是跟踪进入AbstractAutowireCapableBeanFactory类的postProcessObjectFromObjectFactoryBean方法:
pubilc Object applyBeanPostProcessorsAfterInitialization(Object existingBean,String beanName)
这里只需要了解Spring中获取bean的规则中有这样一条:尽可能保证所用bean初始化后都会调用注册的BeanPostProcessor的postProcessAfterInitialization方法进行处理,在实际开发过程中大可以针对这个特性涉及自己的业务逻辑。
5.4 获取单例
之前讲解了从缓存获取单例的过程,那么,如果缓存中不存在已经加载的单例bean就需要从头开始bean的加载过程了,而Spring中使用getSingleton的重载方法实现bean的加载过程。
public Object getSingleton(String beanName, ObjectFactory singletonFactory)
// 全局变量需要同步
// 首先检查对应的bean是否已经加载过,因为singleton模式其实就是复用已创建的bean,所以这一步是必须的
// 如果为空才可以进行singleton的bean的初始化
// 初始化bean
// 加入缓存
上述代码中其实是使用回调方法,使得程序可以在单例创建的前后做一些准备及处理操作,而真正的获取单例bean方法其实并不是在此方法中实现的,其实现逻辑是在ObjectFactory类型的实例singletonFactory中实现的,而这些准备及处理操作包括如下内容:
- 检查缓存是否已经加载过;
- 如没有加载,则记录beanName的正在加载状态;
- 加载单例前记录状态。
可能你会觉得beforeSingletonCreation方法是个空实现,里面没有任何逻辑,但其实不是,这个函数中做了一个很重要的操作,记录加载状态,也就是通过this.singletonsCurrentlyInCreation.add(beanName)将正要创建的bean的记录在缓存中,这样便可以对循环依赖进行检测。
- 通过调用参数传入的ObjectFactory的个体Object方法实例化bean;
- 加载单例后的处理方法调用;
同步骤(3)的记录加载状态相似,当bean加载结束后需要移除缓存中对该bean的正在加载状态的记录。
- 将结果记录至缓存并删除加载bean过程中所记录的各种辅助状态。
- 返回结果
虽然已经从外部了解了加载bean的逻辑架构,但现在并没有开始对bean加载功能的探索,之前提到过,bean的加载逻辑其实是在传入的ObjectFactory类型的产出singletonFactory中定义的,反推参数的获得,得到如下代码:
getSingleton(beanName,new ObjectFactory< Object>()
// return createBean(beanName,mbd,args)
ObjectFactory的核心部分其实只是调用了createBean的方法,所以还需要到createBean方法中寻求真理。
5.5 准备创建bean
我们不可能指望在一个函数中完成一个复杂的逻辑,而且我们跟踪了这么多Spring的代码。经历的这么多函数,或多或少也发现一些规律:一个真正干活的函数其实是以do开头的;而给我们错觉的函数,其实只是从全局的角度去做一些统筹的工作。这个规律对于createBean也不例外,那么createBean函数中做了哪些准备工作:
protected Object createBean(final String beanName,final RootBeanDefinition mbd,final Object[] args) throws BeanCreationException
// 锁定class,根据设置的class属性或者根据className来解析Class
// 验证及准备覆盖的方法
// 给BeanPostProcessors一个机会返回代理来替代真正的实例
// 调用doCreateBean()方法
从代码中可以总结出函数完成的具体步骤及功能
- 根据设置的class属性或者根据className来解析Class;
- 对override属性进行标记及验证。
很多人并不知道这个方法的作用,因为在Spring的配置里面根本就没有注入override-method之类的配置,那么这个方法到底是干什么的呢?
其实在Spring中确实没有override-method这样的配置,但是在Spring配置中存在lookup-method和replace-method的,而这两个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverride属性中,而这个函数的操作就是针对这两个配置的。
- 应用初始化前的后处理器,解析指定bean存在初始化前的短路操作;
- 创建bean。
5.5.1 处理override属性
查看源码中AbstractBeanDefinition类的prepareMethodOverrides方法:
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException
// 获取对应类中方法名的个数
// 如果个数为1时,标记MethodOverride暂未被覆盖,避免参数类型检查的开销
通过以上两个函数的代码实现了之前反复提到过的,在Spring配置中存在lookup-method和replace-method两个配置功能,而这个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性中,这两个功能的实现原理其实是在bean实例化的时候如果检测到存在methodOverrides属性,会动态地为当前bean生成代理并使用对应的拦截器为bean做增强处理。
但是,这里提到的是,对于方法的匹配来讲,如果一个类中存在若干个重载方法,那么在函数调用及增强的时候还需要根据参数类型进行匹配,来最终确认当前调用的到底是哪个函数。但是,Spring将一部分匹配工作在这里完成了,如果当前类方法只有一个,那么就设置重载该方法没有被重载,这样在后续调用的时候便可以直接找到该方法,而不需要进行方法的参数匹配验证了,而且还可以提前对方法存在性进行验证,可谓一箭双雕。
5.5.2 实例化的前置处理
在真正调用doCreate方法创建bean的实例前使用了这样一个方法resolveBeforeInstantiation(beanName,mbd)对BeanDefinition中的属性做前置处理。当然,无论其中是否有相应的逻辑实现我们都可以理解,因为真正的逻辑实现前后留有处理函数也是可扩展的一种体现。但是这并不是最重要的,在函数中还提供了一个短路判断,这才是最关键的部分。
当经过前置处理后返回的结果如果不为空,那么会直接略过后续的Bean的创建而直接返回结果。这个特性判断虽然很容易被忽略,但是却起着至关重要的作用,我们熟知的AOP功能就是基于这里判断的。
protected Object resolveBeforeInstantiation(String beanName,RootBeanDefinition mbd)
此方法中最吸引人的无疑是两个方法:applyBeanPostProcessBeforeInstantiation以及applyBeanPostProcessAfterInitialization。这两个方法实现非常简单,无非是对后处理器中所有InstantiationAwareBeanPostProcessor类型的后处理器进行postProcessBeforeInstantiation方法和BeanPostProcessAfterInitialization方法的调用。
1. 实例化前的后处理器的应用
bean的实例化前调用,就是将AbstractBeanDefinition转换为BeanWrapper前的处理,给子类一个修改BeanDefinition的机会,也就是说当程序经过这个方法后,bean可能已经不是我们认识的bean了,而是或许成为了一个经过处理的代理bean,可能通过cglib生成的,也可以是其他技术生成的。
2. 实例化后的后处理器的应用
在讲解从缓存中获取单例bean的时候提到过,Spring中的规则是在bean的初始化后尽可能保证将注册的后处理器的postProcessAfterInitialization方法应用到该bean中,因为如果返回的bean不为空,那么便不会再次经历普通bean的创建过程,所以只能在这里应用后处理器的postProcessAfterInitialization方法。
5.6 循环依赖
实例化bean是一个非常复杂的过程,而其中最难以理解的就是对循环依赖的解决。
5.6.1 什么是循环依赖
循环依赖就是循环引用,就是两个或多个bean之间的持有对方,比如ClassA引用ClassB,ClassB引用ClassC,ClassC引用ClassA,则最终反应为一个环。此处不是循环调用,循环调用是方法之间的环调用。
循环调用是无法解决的,除非有终结条件,否则就是死循环,最终导致内存溢出错误。
5.6.2 Spring如何解决循环依赖
Spring容器循环依赖包括构造器循环依赖和setter循环依赖,那么Spring容器是如何解决循环依赖的呢?
1. 构造器循环依赖
表示通过构造器注入构造的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。
如何创建TestA类时,构造器需要TestB类,那么将去创建TestB,在创建TestB类时又发现需要TestC类,则去创建TestC,最终在创建TestC时发现又需要TestA,从而形成一个环,没有办法创建。
Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池”中,bean标识符在创建过程中将一直保持在这个池中,因此在创建bean过程中发现自己已经在“当前创建bean池”里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖,而对于创建完毕的bean将从“当前创建bean池”中清除掉。
2. setter循环依赖
表示通过setter注入方式构成的循环依赖,对于setter注入方式构成的循环依赖是通过Spring容器提前暴露刚创建完成构造器注入单位完成其他步骤的bean来完成的,而且只能解决单例作用域下的bean循环依赖。通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean。
3. prototype范围的依赖处理
对于prototype作用域bean,Spring容器无法完成依赖注入,因为Spring容器不进行缓存prototype作用域的bean,因为无法提前暴露一个创建的bean。
对于singleton作用域bean,通过“setAllowCirularReferences(false)”来禁用循环引用。
5.7 创建bean
介绍了循环依赖以及Spring中的循环依赖的处理方法后。当经过resolveBeforeInstantitation方法后,程序有两个选择,如果创建了代理或者重写可InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法并在方法postProcessBeforeInstantiation中改变bean,则直接方法就可以了,否则进行常规bean的创建,而常规bean的创建就是在doCreateBean中完成。
protected Object doCreateBean(final String beanName,final RootBeanDefinition mbd,final Object[] args)
// 根据指定bean使用对应的策略创建新的实例,如:工厂模式、构造函数自动注入、简单初始化等
// 是否提早曝光:单例&允许循环依赖&当前bean是否在创建中,检测循环依赖
// 为了避免后期循环依赖,可以在bean初始化完成钱将创建实例的ObjectFactory加入工厂
// 对bean在一次依赖引用,主要应用SmartInstantiationAwareBeanPostProcessor,其中我们熟知的AOP就是在这里将advice动态织入bean中,如没有则直接返回
// 对bean属性填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖bean
// 调用初始化方法,比如init-method
// earlySingletonReference只检测到有循环依赖的情况下才不会为空
// 如果exposeObject没有在初始化方法中被改变,也就是没有增强
// 检测依赖
// 因为bean创建其所依赖的bean一定是已经创建了,actualDependentBeans不为空则表示当前bean却没有没全部创建完,也就是说存在循环依赖
// 根据scope注册bean
我们来看看整个函数的概要思路:
- 如果是单例则需要首先清除缓存;
- 实例化bean,将BeanDefinition转换为BeanWrapper。
转换是一个复杂的过程,但是可以尝试概括大致的功能
如果存在工厂则使用工厂方法进行初始化话。
一个类有多个构造函数,每个构造函数都有不同的参数,所以需要根据参数锁定构造函数并进行初始化。
如果既不存在工厂方法也不存在带有参数的构造方法,则使用默认的构造函数进行bean的实例化。
- MergedBeanDefinitionPostProcessor的应用,bean合并后的处理,Autowire正是通过此方法实现诸如类型的解析。
- 依赖处理,在Spring中会有依赖循环的情况,例如,当A中含有B的属性,而B中又含有A的属性是就会构成一个循环依赖,此时如果A和B都是单例,那么在Spring中的处理方式就是当创建B的时候,涉及自动注入A的步骤时,并不是直接去再次创建A,而是通过放入缓存中的ObjectFactory来创建实例,这样就解决了循环依赖的问题了。
- 属性填充,将所有属性填充至bean的实例中。
- 循环依赖检查,之前提到过,在Spring中解决循环依赖只对单例有效,而对于prototype的bean,Spring没有好的解决办法,唯一做的就是抛出异常,在这个步骤里面会检测已经加载的bean是否已经出现了依赖循环,并判断是否需要抛出异常。
- 注册DisposeableBean,如果配置了destroy-method,这里需要注册以便于在销毁时候调用。
- 完成创建并返回。
可以看出上面的步骤非常的繁琐,每一步骤使用大量的代码来完成其功能,最复杂也是最难理解的当属循环依赖的处理,在真正进入doCreateBean前有必要先了解下循环依赖。
5.7.1 创建bean的实例
当了解了循环依赖以后就可以深入分析创建bean的每一个步骤了,首先从createBeanInstance开始。
protected BeanWapper createBeanInstance(String beanName,RootBeanDefinition mbd,Object [] args)
// 解析class
// 如果工厂方法不为空则使用工厂方法初始化策略
// 一个类有多个构造函数,每个构造函数都有不同的参数,所有调用前需要先根据参数锁定构造函数或对应的工厂方法
// 如果已经解析过则使用解析好的构造函数方法不需要再次锁定
// 构造函数自动注入
// 使用默认构造函数构造
// 需要根据参数解析构造函数
// 构造函数自动注入
// 使用默认构造函数构造
虽然代码中实例化的细节非常复杂,但是在createBeanInstance方法中还是可以清晰地看到实例化的逻辑
- 如果RootBeanDefinition中存在factoryMethodName属性,或者在配置文件中配置了factory-method,那么Spring会尝试使用instantiateUsingFactoryMethod(beanName,mbd,args)方法根据RootBeanDefinition中的配置生成bean的实例。
- 解析构造函数并进行构造函数的实例化,因为一个bean对应的类中可能有多个构造函数,而每个构造韩式的参数不同,Spring根据参数及类型去判断最终会使用哪个构造函数进行实例化,但是,判断的过程是比较消耗性能的步骤,所以采用缓存机制,如果已经解析过则不需要重复解析而直接从RootBeanDefinition中resolvedConstructorOrFactoryMethod缓存的值去取,否则需要再次解析,并将解析的结果添加至RootBeanDefinition中的属性resolvedConstructorOrFactoryMethod中。
1. autowireConstructor
对于实例的创建Spring中分为两种情况,一种是通用的实例化,另一种是带有参数的实例化。带有参数的实例化过程相当复杂,因为存在着不确定行,所以在判断对应参数上做了大量的工作。
public BeanWrapper autowireConstructor(final String beanName,final RootBeanDefinition mbd,Constructor [] chosenCtors,final Object [] explicitArgs)
// explicitArgs通过getBean方法传入,如果getBean方法调用的时候指定方法参数那么直接使用
// 如果getBean方法时候没有指定则尝试从配置文件中解析
// 尝试从缓存中获取
// 配置的构造函数参数
// 如果缓存中存在
// 解析参数类型,缓存中的值可能是原始值也可能是最终值
// 如果没有缓存
// 提取配置文件中的配置的构造函数参数
// 用于承载解析后的构造函数参数的值
// 能解析到的参数个数
// 排序给定的构造函数,public构造函数优先参数数量降序,非public构造函数参数数量降序
// 如果已找到选用的构造函数或者需要参数个数小于当前构造函数参数给则终止,以为已经按照参数个数降序排列
// 有参数则根据构造对应参数类型的参数
// 注释上获取参数名称
// 获取参数名探索器
// 获取指定构造函数的参数名称
// 根据名称和数据类型创建参数持有者
// 构造函数没有参数的情况
// 探测是否有不确定性的构造函数存在,例如不同构造函数的参数为父子关系
// 将解析的构造函数加入缓存
// 将构造的实例加入BeanWrapper
逻辑很复杂,函数代码量很大,总览一下整个函数,其实现的功能考虑了一下几个方面:
- 构造函数参数确定。
1.1 根据explicitArgs参数判断,如果传入的explicitArgs参数不为空,那便可以直接确认参数,因为explicitArgs参数是在调用Bean的时候用户指定的,在BeanFactory类中存在这样的方法:
Object getBean(String name,Object ... args) throws BeanException
在获取bean的时候,用户不但可以指定bean的名称还可以指定bean所对应类的构造函数或者工厂方法的方法参数,主要用于静态工厂方法的调用,而这里需要给定完全匹配的参数的,所以,便可以判断,如果传入参数explicitArgs不为空,则可以确定构造函数参数就是它。
1.2 缓存中获取,除此之外,确定参数的办法如果之前已经分析过,也就是说构造函数已经记录在缓存中,那么便可以直接拿来使用。而且,这里要提到的是,在缓存中缓存的可能是参数的最终类型也可能是参数的初始类型。
1.3 配置文件获取,如果不能根据传入参数explicitArgs确认构造函数的参数也无法在缓存中得到相关信息,那么只能进行新一轮的分析了。分析从配置文件的构造韩式信息开始,经过之前的分析,Spring配置文件中的信息经过转换会通过BeanDefinition实例承载,也就是参数mbd中包含,那么可以通过调用mbd.getConstructorArgumentValues()来获取配置的构造函数信息。有了配置中的信息便可以获取对应的参数值信息了,获取参数值的信息包括指定值,如:直接指定构造函数中某个值为原始类型String类型,或者是一个对其他bean的引用,而这一处理委托给了resolveConstructorArgments方法,并返回能解析到的参数的个数。- 构造函数的确定,经过第一步后已经确定了构造函数的参数,接下来的任务就是根据构造函数参数在所有的构造函数中锁定对应的构造函数,而且匹配的方法就是根据参数个数匹配,所以在匹配之前需要对构造函数按照public构造函数优先参数数量降序,非public构造函数参数数量降序。这样可以在遍历的情况下迅速判断排在后面的构造函数参数个数是否符合条件,由于在配置文件中并不是唯一限制使用参数位置索引的方式去创建,同样还支持指定参数名称进行设定参数值的情况,那么这种情况就需要首先确定构造函数中的参数名称,获取参数名称可以有两种方式,一种是通过注解的方式直接获取,另一种方式就是使用Spring提供的工具类ParameterNameDiscoverer来获取,构造函数、参数名称、参数类型、参数值、确定后就可以锁定构造函数以及转换对应的参数类型了。
- 根据确定的构造函数转换对应的参数类型,主要是使用Spring中提供的类型转换器或者用户自定义的类型转换器进行转换。
- 构造函数不确定性的验证,当然,有时候及时构造函数、参数名称、参数类型、参数值都确定后也不一定会直接锁定构造函数,不同构造函数的参数为父子关系,所以Spring在最后又做了一次验证。
- 根据实例化策略以及得到的构造函数及构造函数参数实例化Bean。
2. instantiateBean
经历了带有参数的构造函数的实例构造,对于不带参数的构造函数的实例化过程中并没有什么实质性逻辑,带有参数的实例构造中,Spring把精力都放在了构造函数以及参数的匹配上,所以如果没有参数的话那将是非常简单的一件事,直接代用实例化策略进行实例化就可以了。
3. 实例化策略
实例化过程中反复提到过实例化策略,那这又是做什么用的呢?其实,经过前面的分析,已经得到了足以实例化的所有相关信息,完全可以使用最简单的反射方法只讲反射来构造实例对象,但是Spring却没有这么做。
public Object instantiate(RootBeanDefinition beanDefinition,String beanName,BeanFactory owner)
// 如果有需要覆盖或者动态替换的方法则需要使用cglib进行动态代理,因为可以在创建代理的同时将动态方法织入类中,如果没有需要动态改变的方法,为了方法直接反射就可以了。
程序中,首先判断beanDefinition.getOverrides()为空也就是用户没有使用replace或者lookup的配置方法,呢么直接使用反射的方法,简单快捷,但是如果使用了这两特性,在直接使用反射的方式创建实例就不妥了,因为需要将这两个配置提供的功能切入进去,所以必须要使用动态代理的方式将包含两个特性所对应的逻辑的拦截增强器设置进去,这样才可以保证在调用方法的时候会被对应的拦截器增强,返回值为包含拦截器的代理实例。
5.7.2 记录创建bean的ObjectFactory
在doCreate函数中有这样一段代码:
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCirularReference && isSingletonCurrentlyInCreation(beanName));
if(earlySingletonExposure)
// 为避免后期循环依赖,可以在bean初始化完成前创建实例的ObjectFactory加入工厂
// 对bean再一次依赖引用,主要应用SmartInstantiationAwareBeanPostaProcessor,其中AOP就是在这里将advice动态织入bean中,如没有则直接返回bean不做任何处理
这段代码不是很复杂,但是很多人不是太理解这段代码的作用,而且,这段代码仅从此函数中去理解也很难理解其中的含义,需要从全局的接到去思考Spring的依赖解决办法。
earlySingletonExposure:从字面的意思就是提早曝光的单例。需要确认有哪些条件影响这个值。
mbd.isSingleton():没有太多可以解释的,此RootBeanDefinition代表是否是单例。
this.allowCirularReference:是否允许循环依赖,在配置文件中并没有找到如何配置,但是在AbstractRefreshableApplicationContext中提供了设置函数,可以通过硬编码的方法进行设置或者通过自定义名称空间进行设置
isSingletonCurrentlyInCreation(beanName):该bean是否在创建中,在Spring中,会有个专门的属性默认为DefaultSingletonBeanRegistry的isSingletonsCurrentlyInCreation来记录bean的加载状态,在bean开始创建前会将beanName记录在属性中,在bean创建结束后会将beanName从属性中移除,不同scope的记录位置并不一样。
5.7.3 属性注入
在了解循环依赖的时候,populateBean这个函数的主要功能就是属性填充,那么究竟是如何实现填充的呢?
protected void populateBean(String beanName,AbstractBeanDefinition mbd,BeanWrapper bw)
// 没有属性填充则直接返回
// 给InstantiationAwareBeanPostProcessors最后一次机会在属性设置前改变bean,如:可以用来支持属性注入的类型
// 如果后处理器发出停止填充命令则终止后续操作
// 根据名称自动注入
// 根据类型自动注入
// 后处理器已经初始化
// 需要依赖检查
// 对所有需要依赖检查的属性进行后处理
// 将属性应用到bean中
在populateBean函数中提供了这样的处理流程。
- InstantiationAwareBeanPostProcessors处理器的postProcessAfterInstantiation函数的应用,此函数可以控制程序是否继续进行属性填充。
- 根据注入类型(byName/byType),提取依赖的bean,并统一存到PropertyValues中。
- 应用InstantiationAwareBeanPostProcessors处理器的postProcessPropertyValues方法,对属性获取完毕填充前对属性再次处理,典型应用是RequiredAnnotationBeanPostProcessor类中对属性的验证。
- 将所有PropertyValues中的属性天充值BeanWrapper中。
1. autowireByName
上面提到根据注入类型(byName/byType),提取依赖的bean,并统一存入PropertyValues中,那么首先了解下byName功能是如何实现的。
prototype void autowireByName(String beanName,AbstractBeanDefinition mbd,BeanWrapper bw,MutablePropertyValues pvs)
// 寻找bw中需要依赖注入的属性
// 递归初始化相关的bean
// 注册依赖
2. autowireByType
autowireByType与autowireByName对于我们理解和使用来说复杂程度都很相似,但是实现功能的复杂度却完全不一样。
protected void autowireByType(String beanName,AbstractBeanDefinition mbd,BeanWrapper bw,MutablePropertyValues pvs)
// 寻找bw中需要依赖注入的属性
// 探索指定属性的set方法
// 解析指定beanName的属性匹配的值,并把解析到的属性名称存储在autowireBeanName中,当属性存在多个封装bean时,将会找到所有的bean的类型将其注入
// 注册依赖
实现根据名称自动匹配的第一步是寻找bw中需要依赖注入的属性,同样对于根据类型自动匹配的实现来讲第一步也是寻找bw中需要依赖注入的属性,然后遍历这些属性并寻找类型匹配的bean,其中最复杂的就是寻找匹配的bean,同时,Spring中提供了对集合的类型注入的支持。
对于寻找类型匹配的逻辑实现封装在了resolveDependency函数中。
public Object resolveDependency(DependencyDescriptor,String beanName,Set< String> autowiredBeanNames,TypeConverter typeConverter)
// ObjectFactory类注入的特殊处理
// javaxInjectProviderClass类注入的特殊处理
// 通用处理逻辑
// 用于支持Spring中新增的注解@Value
// 如果解析器没有解析成功,则需要考虑各种情况
// 属性是数组类型
// 根据属性类型找到beanFactory中所有类型的匹配bean
// 返回值构成为:key = 匹配的beanName,value = beanName对应的实例化后的bean(通过getBean(beanName)返回)
// 如果autowire的require属性为true而找到而找到的匹配项却为空则抛出异常
// 通过转换器将bean的值转换为对应的type类型
// 属性为Collection类型
// 属性为Map类型
// 已经可以确定只有一个匹配项
寻找类型的匹配执行顺序时,首先尝试使用解析器进行解析,如果解析器没有成功解析,那么可能是使用默认解析器没有做任何处理,或者使用了自定义的解析器,但对于集合等类型来说并不在解析范围之内,所以再次对不同类型进行不同情况的处理,虽然对于不同类型处理方式不一致,但是大致的思路还是很相似的,所以函数中只对数组进行了详细注释。
3. applyPropertyValues
程序运行到这里,已经完成了对所有注入属性的获取,但是获取的属性是以PropertyValues的形式存在,并没有应用到已经实例化的bean中,这一工作是在applyPropertyValues中。
protected void applyPropertyValues(String beanName,BeanDefinition mbd,BeanWapper bw,PropertyValues pvs)
如果mpvs中的值已经被转换为对应的类型那么可以直接设置到beanwapper中
mpvs = (MutablePropertyValues) pvs
如果pvs并不是使用MutablePropertyValues封装的类型,那么直接使用原始属性获取方法
获取对应的解析器
遍历属性,将属性转换为对应类型的对应属性的类型
5.7.4 初始化bean
在配置bean是bean中有一个init-method属性,这个属性的作用是在bean实例化之前调用init-method指定的方法来根据业务进行相应的实例化。我们现在就已经进入到这个方法,首先看一下这个方法的执行位置,Spring程序已经执行过bean的实例化了,并且进行了属性填充,而就在这时将会调用用户设定的初始化方法。
protected Object initialzeBean(final String beanName,final Object bean,RootBeanDefinition mbd)
invokeAwaeMethod(beanName,bean)
对特殊bean的处理:Aware、BeanClassLoaderAware、BeanFactoryAware
// 应用后处理器
// 激活用户自定义的init方法
// 后处理器应用
虽然说此函数的主要目的是进行客户设定的初始化方法的调用,但是除此之外还有些其他必要的工作。
1. 激活Aware方法
在分析原理之前,我们先了解一下Aware的使用。Spring中提供一些Aware相关接口,比如:BeanFactoryAware、ApplicationContextAware、ResourceLoaderAware、ServletContextAware等,实现这些Aware接口的bean在初始化之后,可以取得一些相应的资源,例如实现BeanFactoryAware的bean在初始化后,Spring容器将会注入BeanFactory的实例,而实现ApplicationContextAware的bean,在bean被初始化后,将会被注入ApplicationContext的实例等。
2. 处理器的应用
BeanPostProcess是Spring中开放式架构中一个必不可少的亮点,给用户充足的权限更改或者扩展Spring,而除了BeanPostProcessor还有其他的PostProcessor,当然大部分都是以此为基础的,继承自PostPostProcessor。BeanPostProcessor的使用位置就是这里,在调用客户自定义初始化方法之前以及调用自定义初始化方法后分别会调用BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法,使客户可以根据自己的业务需求进行响应的处理。
3. 激活自定义的init方法
客户定制的初始化方法除了使用配置init-method外,还用使用自定义bean实现InitializingBean接口,并在afterPropertiesSet中实现自己的初始化业务逻辑。
init-method和afterPropertiesSet都是在初始化bean时执行,执行顺序是afterPropertiesSet先执行,而init-method后执行。
在invokeInitMethods方法中就实现了这两个步骤的初始化方法调用。
protected void invokeInitMethods(String beanName,final Object bean,RootBeanDefinition mbd)
// 首先会先检查是否是InitializingBean,如果是的话需要调用afterPropertriesSet方法
// 属性初始化后的处理
// 调用自定义初始化方法
5.7.5 注册DisponsableBean
Spring中不但提供了对于初始化方法的扩展入口,同样也提供了销毁方法的扩展入口,对于销毁方法的扩展,除了熟知的destory-method方法外,用户还可以注册后处理器DestructionAwareBeanPostProcessor来统一处理bean的销毁方法。
// 单例模式下注册需要销毁的bean,此方法中会处理实现DisposableBean的bean,并对所有的bean使用DestructionAwareBeanPostProcessor处理DisposableBean DestructionAwareBeanPostProcessors。
// 自定义scope的处理