本文已收录【修炼内功】跃迁之路
书接上文,在 BeanDefinitionReader 一文中简单介绍了XMLBeanFactory解析xml配置、并注册BeanDefinition的逻辑,本文就bean的实例化过程及销毁做简要分析
先放一张大图(点击图片放大查看,右键或长按保存后更清晰),展示完整的bean创建过程及销毁过程,如果对spring原理有一些理解可将此图作为开发过程中的参考,如果对spring原理还有一些模糊可继续向下阅读(长文预警!)
BeanDefinition
通过前文简单了解到,Spring在初始化过程中并不是直接实例化bean,而是先收集所有bean的元数据信息并注册,bean的元数据描述为接口BeanDefinition
,该接口定义了你能想到的一切有关bean的属性信息
BeanDefinition衍生出一系列实现类
-
AbstractBeanDefinition
如同其他Spring类,大部分BeanDefinition接口的逻辑都由该抽象类实现
-
GenericBeanDefinition
GenericBeanDefinition是一站式、用户可见的bean definition,如何理解“用户可见”?
可见的bean definition意味着可以在该bean definition上定义post-processor来对bean进行操作
-
RootBeanDefinition
当bean definition存在父子关系的时候,RootBeanDefinition用来承载父元数据的角色(也可独立存在),同时它也作为一个可合并的bean definition使用,在Spring初始化阶段,所有的bean definition均会被(向父级)合并为RootBeanDefinition,子bean definition(GenericBeanDefinition/ChildBeanDefinition)中的定义会覆盖其父bean definition(由parentName指定)的定义
-
ChildBeanDefinition
当bean definition存在父子关系的时候,ChildBeanDefinition用来承载子元数据的角色(也可独立存在),在Spring推出GenericBeanDefinition后,其完全可以被GenericBeanDefinition替代,目前使用场景已经非常少
-
AnnotatedBeanDefinition
如其名,主要用来定义注解场景的bean definition
-
ScannedGenericBeanDefinition
主要用来定义@Component、@Service等bean definition,其AnnotationMetadata metadata属性用来存储该bean的类注解信息
-
AnnotatedGenericBeanDefinition
与ScannedGenericBeanDefinition不同的是,其主要用来定义@Configuration等配置类中@Bean的bean definition,其AnnotationMetadata metadata属性与ScannedGenericBeanDefinition相同,MethodMetadata factoryMethodMetadata属性用来存储@Bean描述的方法信息
-
ScannedGenericBeanDefinition
BeanDefinitionHolder只是简单捆绑了BeanDefinition、bean-name、bean-alias,用于注册BeanDefinition及别名alias
BeanRegistry
在一般工程中,bean的定义分散在各种地方(尤其使用注解之后),Spring并不能在解析出每一个bean的元数据信息后立即对该bean做实例化动作,对于依赖的分析与注入、类(方法)的代理、功能上的扩展等,必须等所有的bean元数据全部解析完成之后才能进行
在bean元数据解析完成之后、bean实例化之前,对bean的元数据信息有一个暂存的过程,这个过程便是bean的注册
bean的注册逻辑分两步,一为BeanDefinition的注册,一为别名的注册
- BeanDefinition注册的定义在BeanDefinitionRegistry#registerBeanDefinition,其实现使用一个Map<String, BeanDefinition>来保存bean-name和BeanDefinition的关系
- 别名的注册定义在AliasRegistry#registerAlias,其实现同样使用一个Map<String, String>来保存别名alias-name和bean-name(或另一个别名alias-name)的关系
在完成bean的元数据注册之后,便是根据详尽的元数据信息进行实例化了
BeanFactory
bean的实例化过程比较复杂(Spring考虑到了各种场景),附上BeanRegistry&BeanFactory相关的类依赖图
初看此图,请不要失去信心和耐心,图中各类的作用会一一讲解(见 #附录#相关类说明),这里先介绍几个核心的接口
-
AliasRegistry
bean别名注册和管理
-
BeanDefinitionRegistry
bean元数据注册和管理
-
SingletonBeanRegistry
单例bean注册和管理
-
BeanFactory
bean工厂,提供各种bean的获取及判断方法
大致浏览上图(类依赖图)不难发现,核心实现落在了DefaultListableBeanFactory
,我们以此类作为切入点分析bean实例化过程
bean的实例化过程发生在getBean
调用阶段(对于singleton则发生在首次调用阶段),getBean的实现方法众多,我们追根溯源,找到最通用的方法AbstractBeanFactory#doGetBean
// org.springframework.beans.factory.support.AbstractBeanFactory
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 1. 获取真正的beanName
final String beanName = transformedBeanName(name);
Object bean;
// 2. 尝试获取(提前曝光的)singleton bean实例(为了解决循环依赖)
Object sharedInstance = getSingleton(beanName);
// 3. 如果存在
if (sharedInstance != null && args == null) { ... }
// 4. 如果不存在
else { ... }
// 5. 尝试类型转换
if (requiredType != null && !requiredType.isInstance(bean)) { ... }
return (T) bean;
}
bean的实例化过程虽然复杂,但大体逻辑非常清楚
接下,就以上五个子流程(蓝色部分)一一展开
在实例化bean的过程当中,Spring会使用大量的中间态来判断、处理各种场景和情况,此处先行列出Spring所使用的一些关键的中间态(各中间态的作用会在下文介绍,见 #附录#中间态说明),以便在下文中更好地理解bean实例化过程中对各种情况的判断和处理逻辑
bean name转换
在使用bean-name获取bean的时候,除了可以使用原始bean-name之外,还可以使用alias别名等,bean-name的转换则是将传入的‘bean-name’一层层转为最原始的bean-name
Return the bean name, stripping out the factory dereference prefix if necessary, and resolving aliases to canonical names.
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
函数canonicalName的作用则是利用别名注册aliasMap,将别名alias转为原始bean-name
函数transformedBeanName比较特殊,其是将FactoryBean的bean-name前缀 '&' 去除(BeanFactory#FACTORY_BEAN_PREFIX 下文会介绍)
尝试获取单例
拿到原始的bean-name之后,便可以实例化bean或者直接获取已经实例化的singleton-bean,此处为什么叫‘尝试’获取呢?
在获取singleton-bean的时候一般存在三种情况:1. 还未实例化(或者不是单例);2. 已经实例化;3. 正在实例化;
- 对于 “1. 还未实例化” ,返回null即可,后续进行实例化动作
- 对于“2. 已经实例化”,直接返回实例化的singleton-bean
- 对于“3. 正在实例化”,则较难理解
Spring中对于singleton-bean,有一个sharedInstance的概念,在调用getSingleton
函数时,返回的不一定是完全实例化的singleton-bean,有可能是一个中间状态(创建完成,但未进行属性依赖注入及其他后处理逻辑),这种中间状态会通过getSingleton函数提前曝光出来,目的是为了解决循环依赖(下文会详细介绍循环依赖)
在实例化beanA的过程中,需要依赖beanB和beanC,如果beanC同时又依赖beanA,则需要beanA在实例化完成之前提前曝光出来,以免造成beanA等待beanC实例化完成,beanC等待beanA实例化完成,类似一种死锁的状态
在继续进行之前,有必要简单介绍几个中间态(详见 #附录#中间态说明)
-
singletonObjects
缓存已经实例化完成的singleton-bean
-
earlySingletonObjects
缓存正在实例化的、提前曝光的singleton-bean,用于处理循环依赖
-
singletonFactories
缓存用于生成earlySingletonObject的 ObjectFactory
ObjectFactory,定义了一个用于创建、生成对象实例的工厂方法
@FunctionalInterface public interface ObjectFactory<T> { T getObject() throws BeansException; }
介绍了上述之后,再来描述getSingleton
的逻辑就会比较清楚
不用纠结上述中间态的值是何时被设置进去的,下文会逐步提及
FactoryBean处理(sharedInstance存在的逻辑)
上述 sharedInstance 一定是我们需要的bean实例么?未必!
定义bean的时候可以通过实现FactoryBean接口来定制bean实例化的逻辑,以此增加更多的灵活性及可能性(How to use the Spring FactoryBean?)
@Bean(initMethod = "init", destroyMethod = "destroy")
public FactoryBean myBean() {
return new FactoryBean<MyBean>() {
/**
* 定制bean初始化逻辑
*/
@Override
public MyBean getObject() throws Exception {
MyBean myBean = new MyBean();
// ... 定制化的逻辑
return myBean;
}
/**
* 真正的bean类型
*/
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
}
通过注册FactoryBean类型的bean,实例化后的原始实例类型同样为FactoryBean,但我们需要的是通过FactoryBean#getObject方法得到的实例,这便需要针对FactoryBean做一些处理,这也是接下来要讲解的函数AbstractBeanFactory#getObjectForBeanInstance
Get the object for the given bean instance, either the bean instance itself or its created object in case of a FactoryBean.Now we have the bean instance, which may be a normal bean or a FactoryBean.
If it's a FactoryBean, we use it to create a bean instance.
该函数要实现的逻辑比较简单,如果sharedInstance是 FactoryBean,则使用getObject
方法创建真正的实例
getObjectForBeanInstance是一个通用函数,并不只针对通过getSingleton得到的sharedInstance,任何通过缓存或者创建得到的 rawInstance,都需要经过getObjectForBeanInstance处理,拿到真正需要的 beanInstance
简单介绍getObjectForBeanInstance函数的入参
/**
* @param beanInstance sharedInstance / rawInstance,可能为FactoryBean
* @param name 传入的未做转换的 bean name
* @param beanName 对name做过转换后的原始 canonical bean name
* @param mbd 合并后的RootBeanDefinition,下文会介绍
*/
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
getObjectForBeanInstance函数的处理逻辑
上图中有一个逻辑判断,如果入参name以'&' (BeanFactory#FACTORY_BEAN_PREFIX)开头则直接返回(BeanFactory)
这里兼容了一种情况,如果需要获取/注入FactoryBean而不是getObject生成的实例,则需要在bean-name/alias-name前加入'&'
/**
* 注入FactoryBean#getObject生成的实例
*/
@Autowired
private MyBean myBean;
/**
* 直接注入FactoryBean
*/
@Resource(name = "&myBean")
private FactoryBean<MyBean> myFactoryBean;
对于singleton,FactoryBean#getObject的结果会被缓存到factoryBeanObjectCache,对于缓存中不存在或者不是singleton的情况,会通过FactoryBean#getObject生成(上图中蓝色未展开的逻辑)
Spring并非简单的调用FactoryBean#getObject,而是分为两部分处理
bean instance生成
上图中doGetObjectFromFactoryBean,主要对getObject方法进行了包装,判断是否需要在SecurityManager框架内执行以及对null结果进行封装(NullBean)
bean instance后处理
上图中postProcessObjectFromFactoryBean,主要对生成的bean instance做一些后处理(可以跟踪到AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization),
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 拿到所有注册的BeanPostProcessor,执行后处理动作
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
postProcessAfterInitialization函数可以对现有bean instance做进一步的处理,甚至可以返回新的bean instance,这就为bean的增强提供了一个非常方便的扩展方式(可以思考一下,AOP的代理类是如何生成的)
加载bean实例(sharedInstance不存在的逻辑)
以上,讨论了bean instance存在于缓存中的情况,如果缓存中不存则需要进行bean的加载
简单来讲,bean的加载/创建分为三大部分
- 将BeanDefinition合并为RootBeanDefinition
这里类似类继承,子BeanDefinition属性会覆盖父BeanDefinition
- 依次加载所依赖的bean
对于有依赖的情况,优先递归加载依赖的bean
- 按照不同的bean类型,根据BeanDefinition的定义进行加载/创建
BeanDefinition合并(RootBeanDefinition)
将BeanDefinition转为RootBeanDefinition,如果存在父子关系,则进行合并
这里不再赘述,可以参考 AbstractBeanFactory#getMergedLocalBeanDefinition
加载depends-on beans
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 遍历所有的依赖
for (String dep : dependsOn) {
// 检测循环依赖
if (isDependent(beanName, dep)) { /* throw exception */ }
// 注册依赖关系
registerDependentBean(dep, beanName);
// 递归getBean,加载依赖bean
try { getBean(dep); }
catch (NoSuchBeanDefinitionException ex) { /* throw exception */ }
}
}
逻辑很简单,但这里涉及到两个中间态dependentBeanMap、dependenciesForBeanMap
- dependentBeanMap
存储哪些bean依赖了我(哪些bean里注入了我)
如果 beanB -> beanA, beanC -> beanA,key为beanA,value为[beanB, beanC]
- dependenciesForBeanMap
存储我依赖了哪些bean(我注入了哪些bean)
如果 beanA -> beanB, beanA -> beanC,key为beanA,value为[beanB, beanC]
理解两者的存储关系,有助于在阅读源码的过程中理解bean的加载和销毁顺序
加载singleton bean实例
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
// singletonFactory - ObjectFactory
try { return createBean(beanName, mbd, args); }
catch (BeansException ex) { destroySingleton(beanName); throw ex; }
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
这里涉及两个比较核心的函数createBean
、getObjectForBeanInstance
-
createBean
根据BeanDefinition的内容,创建/初始化bean instance
-
getObjectForBeanInstance
上文已经介绍过,主要处理FactoryBean,将FactoryBean转为真正需要的bean instance
createBean被包装在lambda(singletonFactory)中作为getSingleton的参数,我们来看getSingleton的实现逻辑
所以,关键的逻辑在createBean函数中,bean的创建逻辑较为复杂,我们放到后面介绍
加载prototype bean实例
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally { afterPrototypeCreation(beanName); }
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
prototype bean的创建与singleton bean类似,只是不会缓存创建完成的bean
加载其他scope bean实例
scope,即作用域,或者可以理解为生命周期
上文介绍了singleton-bean及prototype-bean的创建过程,严格意义上讲以上两种都是一种特殊的scope-bean,分别对应ConfigurableBeanFactory#SCOPE_SINGLETON及ConfigurableBeanFactory#SCOPE_PROTOTYPE,前者作用域为整个IOC容器,也可理解为单例,后者作用域为所注入的bean,每次注入(每次触发getBean)都会重新生成
Spring中还提供很多其他的scope,如WebApplicationContext#SCOPE_REQUEST或WebApplicationContext#SCOPE_SESSION,前者作用域为一次web request,后者作用域为一个web session周期
自定义scope的bean实例创建过程与singleton bean的创建过程十分相似,需要实现Scope的get方法(org.springframework.beans.factory.config.Scope#get),
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) { /* throw exception */ }
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
// createBean被封装在Scope#get函数的lambda参数ObjectFactory中
try { return createBean(beanName, mbd, args); }
finally { afterPrototypeCreation(beanName); }
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException ex) { /* throw exception */}
}
Scope接口除了get方法之外,还有一个remove方法,前者用于定义bean的初始化逻辑,后者用于定义bean的销毁逻辑
public interface Scope {
/**
* Return the object with the given name from the underlying scope
*/
Object get(String name, ObjectFactory<?> objectFactory);
/**
* Remove the object with the given name from the underlying scope.
*/
Object remove(String name);
}
WebApplicationContext#SCOPE_SESSION对应的Scope实现见org.springframework.web.context.request.SessionScope
WebApplicationContext#SCOPE_REQUEST对应的Scope实现见org.springframework.web.context.request.RequestScope
以上两种Scope实现都较为简单,前者将初始化的bean存储在request attribute种,后者将初始化的bean存储在http session中,具体细节请自行查阅源码
Q: Spring中实现了哪些Scope?又是什么时候注册的?
Bean创建过程
AbstractAutowireCapableBeanFactory#createBean
了解bean创建的过程也是一个抽丝剥茧的过程,真正创建的过程封装在AbstractAutowireCapableBeanFactory#doCreateBean中,而在此之前有一些准备工作,整体流程如下图
-
resolveBeanClass
这一步骤用于锁定bean class,在没有显示指定beanClass的情况下,使用className加载beanClass
-
验证method overrides
在 [[spring-framework] [2] BeanDefinitionReader](https://segmentfault.com/a/11... 一文中有提到过lookup-method及replace-method,该步骤是为了确认以上两种配置中的method是否存在
-
执行InstantiationAwareBeanPostProcessor前处理器(postProcessBeforeInstantiation)
这里要特别注意的是,如果这个步骤中生成了“代理”bean instance,则会有一个短路操作,
直接返回
该bean instance而不再执行doCreate,这是一个“细思极恐”的操作,但在一些特殊场景(尤其框架之中)提供了良好的扩展机制try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { // 如果这里生成了代理的bean instance会直接返回 return bean; } } cache (Throwable ex) { // throw exception } try { // 创建bean instance Object beanInstance = doCreateBean(beanName, mbdToUse, args); // ... }
Q: InstantiationAwareBeanPostProcessor的使用场景有哪些?Spring有哪些功能使用了InstantiationAwareBeanPostProcessor?它们是在什么时候注册的?
-
doCreateBean (AbstractAutowireCapableBeanFactory)
真正bean的创建及初始化过程在此处实现,但Spring对bean创建及初始化逻辑的复杂程度完全超出了本篇文章之承载,这里只对一些关键的逻辑做梳理
创建bean实体
AbstractAutowireCapableBeanFactory#createBeanInstance
从上面的流程图可以看出,创建bean实体不一定会使用到构造函数,有两个特殊的方式
1. instance supplier
AbstractAutowireCapableBeanFactory#obtainFromSupplier
从Spring5开始,多了一种以函数式注册bean的方式(参考https://www.baeldung.com/spri...)
// 注册MyService
context.registerBean(MyService.class, () -> new MyService());
// 注册MyService,并指定bean name
context.registerBean("mySecondService", MyService.class, () -> {
MyService myService = new MyService();
// 其他的逻辑
return myService;
});
// 注册MyService,指定bean name,并增强bean definition
context.registerBean("myCallbackService", MyService.class, () -> {
MyService myService = new MyService();
// 其他的逻辑
return myService;
}), bd -> bd.setAutowireCandidate(false));
或者
// 构建BeanDefinition
BeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MyService.class, () -> {
MyService myService = new MyService();
// 其他的逻辑
return myService;
});
// 注册BeanDefinition
beanFactory.registerBeanDefinition("myService", bd);
通过以上方式注册的bean,Spring会调用该supplier生成bean实体
2. factory method
AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod
ConstructorResolver#instantiateUsingFactoryMethod
在xml配置中还有一种不太常见的bean注册方式
public class MyHome {
// 静态方法
public static MyHome create() {
return new MyHome();
}
}
public class MyFactory {
// 非静态方法
public MyHome create() {
return new MyHome();
}
}
<bean id="myFactory" class="com.manerfan.MyFactory"></bean>
<!-- 方式一 -->
<!-- 使用指定类的静态方法 -->
<bean id="myHome1"
class="com.manerfan.MyHome"
factory-method="create"></bean>
<!-- 方式二 -->
<!-- 使用指定bean的非静态方法 -->
<bean id="myHome2"
class="com.manerfan.MyHome"
factory-method="create"
factory-bean="myFactory"></bean>
Spring会通过指定类的指定方法生成bean实体,其中有两种方式,一种方式(仅指定factory-method)使用指定类的静态方法生成,另一种方式(同时指定factory-method和factory-bean)使用指定bean的非静态方法生成
同时factory-method中还允许传入一些参数,如果存在同名函数,Spring会根据参数的个数及类型找到匹配的method
public class MyHome {
private MyHouse house;
private MyCar car;
// setters
}
public class MyFactory {
// 携带入参
public MyHome create(MyHouse house, MyCar car) {
MyHome myHome = new MyHome();
myHome.setHouse(house);
myHome.setCar(car)
return myHome;
}
// 同名函数
public MyHome create(MyHouse house) {
MyHome myHome = new MyHome();
myHome.setHouse(house);
myHome.setCar(defaultCar)
return myHome;
}
}
<bean id="myHome2"
class="com.manerfan.MyHome"
factory-method="create"
factory-bean="myFactory">
<!-- 这里使用的是构造函数参数类型 -->
<constructor-arg name="house" ref="house"/>
<constructor-arg name="car" ref="car"/>
</bean>
这样的代码是不是让你想到了@Configuration中的@Bean,@Bean所修饰方法如果存在参数的话,Spring会通过参数的类型及名称自动进行依赖注入
@Configuration
public class MyFactory {
@Bean
public MyHome create(MyHouse house, MyCar car) {
MyHome myHome = new MyHome();
myHome.setHouse(house);
myHome.setCar(car)
return myHome;
}
}
我们可以大胆猜测,@Configuration + @Bean的实现方式就是factory-bean + factory-method,在后文介绍Spring的注解体系时会揭晓
使用指定(类)bean的(静态)方法创建bean实体的逻辑在ConstructorResolver#instantiate(String, RootBeanDefinition, Object, Method, args),而真正的逻辑在SimpleInstantiationStrategy#instantiate(RootBeanDefinition, String, BeanFactory, Object, Method, Object...),其核心的执行逻辑非常简单,有了方法factoryMethod(factoryBean)及入参args,便可以调用该方法创建bean实体
Object result = factoryMethod.invoke(factoryBean, args);
factoryBean可以通过beanFactory.getBean获取到(正是当前在讲的逻辑),factoryMethod可以通过反射获取到,入参args如何获取?这便涉及到Spring中的一个重要概念 -- 依赖注入,如何准确的找到依赖的实体并将其注入,便是接下来的重点,这里涉及到一个非常重要的函数ConstructorResolver#resolvePreparedArguments,该函数的作用是将BeanDefinition中定义的入参转换为真是需要的参数(xml中定义的或者注解中定义的),在 BeanDefinitionReader 一文中有过介绍,ref
会被封装为RuntimeBeanReference
存储、value
会被封装为TypedStringValue
存储等等,如何将这些封装好的存储类型转为真正需要的函数参数,便是ConstructorResolver#resolvePreparedArguments函数的作用
这里分成了三大分支
-
resolveValueIfNecessary
针对BeanMetadataElement,进行值的转换,其中又会包含特别细的分支,大致如下
- RuntimeBeanNameReference
AbstractBeanFactory#evaluateBeanDefinitionString
支持Spl表达式解析bean name
- BeanDefinitionHolder 、BeanDefinition
BeanDefinitionValueResolver#resolveInnerBean
与createBean函数的逻辑类似,创建一个inner bean
- DependencyDescriptor
AutowireCapableBeanFactory#resolveDependency
用来处理OptionalBean、LazyBean、AutowireCandidateBean等(详见下文“注解注入”一节)
- ManagedArray、ManagedList、ManagedSet、ManagedMap
BeanDefinitionValueResolver#resolveManagedArray
BeanDefinitionValueResolver#resolveManagedList
BeanDefinitionValueResolver#resolveManagedSet
BeanDefinitionValueResolver#resolveManagedMap
内部递归使用resolveValueIfNecessary方法获取bean并最终封装成对应的类型
- ManagedProperties
通过BeanDefinitionValueResolver#evaluate(Spel)计算value的值,最终封装为Properties
- TypedStringValue
通过BeanDefinitionValueResolver#evaluate(Spel)计算value的值
- RuntimeBeanNameReference
对于这部分内容,Spring在接下来的发展中可能还会不断地扩充
- String
AbstractBeanFactory#evaluateBeanDefinitionString
与resolveValueIfNecessary中的RuntimeBeanNameReference一致,支持Spl表达式解析表达式
-
其他
-
InjectionPoint
使用ThreadLocal提供当前bean被注入到的注入点,可以参考 https://www.baeldung.com/what...
@Bean @Scope("prototype") public Logger logger(InjectionPoint injectionPoint) { // return MyComponent.class return Logger.getLogger(injectionPoint.getMethodParameter().getContainingClass()); } @Component public class MyComponent { @Autowired private Logger logger; }
- 其他
与resolveValueIfNecessary中的DependencyDescriptor一致,用来处理OptionalBean、LazyBean等
-
需要留意的是,上述在进行依赖注入的过程中,都会调用DefaultSingletonBeanRegistry#registerDependentBean方法,将各bean之间的依赖关系保存起来,如同前文在介绍加载depends-on时一致,bean之间的依赖关系会分别存放在dependentBeanMap及dependenciesForBeanMap之中(下文在介绍属性注入的时候亦是如此),这对于bean销毁顺序的理解起着至关重要的作用
在返回之前还有convertIfNecessary的方法调用,该函数是将上述解析得到的值转换为函数参数真正的类型
为何要转换?其实上述过程拿到的值并非真正需要的值,如
public class MyComponent {
private Resource initSql;
}
<bean id="myComponent" class="com.manerfan.MyComponent">
<property name="initSql" value="classpath:/init/sql/init.sql"></property>
</bean>
或者
public class MyComponent {
@Value("${init.sql}")
// or @Value("classpath:/init/sql/init.sql")
private Resource initSql;
}
init.sql=classpath:/init/sql/init.sql
不论哪种形式,在convertIfNecessary之前解析到的值都是字符串 "classpath:/init/sql/init.sql",convertIfNecessary的作用便是将上述解析得到的值转换为函数参数真正的类型Resource
convert的逻辑在TypeConverterDelegate#convertIfNecessary,其内部基本的逻辑为
- 找到合适的PropertyEditor (propertyEditorRegistry)
- 使用PropertyEditor的setValue/getValue方法进行转换
将url转换为Resource的PropertyEditor对应为 org.springframework.core.io.ResourceEditor,正是使用ResourceEditor将 字符串"classpath:/init/sql/init.sql”转为对应的Resource
Q: Spring默认的PropertyEditor有哪些?又是什么时候注册的?
3. 有参构造函数
AbstractAutowireCapableBeanFactory#autowireConstructor
ConstructorResolver#autowireConstructor
使用有参构造函数创建bean实例的一个点在于寻找与参数相对应的构造函数(可能定义了多个构造函数),而对于参数的解析和转换(参数的依赖注入)则与使用factory method一样,调用ConstructorResolver#resolvePreparedArguments函数进行处理,这里不再重复描述
在拿到真实的入参及对应的构造函数后,下一步便是使用构造函数来创建bean实例,但事情貌似也并没有那么简单
实例化的过程在ConstructorResolver#instantiate,内部并没有统一利用反射技术直接使用构造函数创建,而是分为两种情况
一种,没有设置override-method时,直接使用构造函数创建
一种,在设置了override-method时,使用cglib技术构造代理类,并代理override方法
以上,Spring默认的实例化策略为CglibSubclassingInstantiationStrategy
4. 无参构造函数
AbstractAutowireCapableBeanFactory#instantiateBean
无参构造函数创建bean实例的过程与有参构造函数创建过程完全一致,只是少了参数的依赖注入,使用默认无参构造函数进行实例化
BeanDefinition后处理
AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
在属性注入之前提供一次机会来对BeanDefinition进行处理,内部执行所有注册MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法
在阅读源码时注意到一个MergedBeanDefinitionPostProcessor的实现类 AutowiredAnnotationBeanPostProcessor,深入到实现内部AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata,其实现了两个注解类的解析 @Value 及 @Autowired ,找到注解修饰的Filed或者Method并缓存,具体的逻辑会在属性注入一节中详细介绍
Q: Spring注册了哪些MergedBeanDefinitionPostProcessor?它们都是做什么用的?又是什么时候注册的?
提前暴露实体
DefaultSingletonBeanRegistry#addSingletonFactory -> AbstractAutowireCapableBeanFactory#getEarlyBeanReference
还记得上文介绍的“尝试获取单例”(AbstractBeanFactory.getSingleton)么?为了解决循环依赖会将singleton-bean提前暴露出来,暴露的逻辑会封装为ObjectFactory(AbstractAutowireCapableBeanFactory#getEarlyBeanReference实现)缓存在DefaultSingletonBeanRegistry.singletonFactories中,在getBean的逻辑getSingleton中会执行ObjectFactory的逻辑将singleton提前暴露
此时暴露的singleton-bean仅完成了bean的实例化,属性注入、初始化等逻辑均暂未执行
属性注入
AbstractAutowireCapableBeanFactory#populateBean
在“创建bean实体”小节中介绍了factory method方式及有参构造函数方式的参数注入逻辑,除此之外还有一种注入便是属性注入
流程图中两次出现了InstantiationAwareBeanPostProcessor,还记得在“Bean创建过程”小结中介绍的InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation么?如果该步骤生成了“代理”bean instance,则会有一个短路操作,直接返回
该bean instance而不再执行后续的doCreate
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation同样是一个短路操作,如果有任意一个InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法返回false,则会跳出属性注入的逻辑,官方对此的解释如下
Give any InstantiationAwareBeanPostProcessors the opportunity to modify the state of the bean before properties are set. This can be used, for example, to support styles of field injection.
autowireByName及autowireByType方法作为“候补”补充BeanDefinition的propertyValues
Fill in any missing property values with references to other beans.
PropertyValue中记录了需要注入的属性信息及需要注入的属性值,那BeanDefinition的propertyValues都来自哪里?xml中的bean配置、自定义的BeanDefinition等
public class MyService {
/**
* string
*/
private String name;
/**
* resource
*/
private Resource res;
/**
* bean ref
*/
private MyComponent myComponent;
}
xml中定义PropertyValue
<bean id="myMyService" class="com.manerfan.MyService">
<property name="name" value="SpringDemoApp"></property>
<property name="res" value="classpath:/init/init.sql"></property>
<property name="myComponent" ref="myComponent"></property>
</bean>
BeanDefinition中直接定义PropertyValue
// 构建BeanDefinition
BeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(MyService.class)
.addPropertyValue("name", "${spring.application.name}")
.addPropertyValue("res", "classpath:/init/init.sql")
.addPropertyReference("myComponent", "myComponent")
.getBeanDefinition();
// 注册BeanDefinition
beanFactory.registerBeanDefinition("myService", bd);
所有的ProperValue均会在AbstractAutowireCapableBeanFactory#applyPropertyValues中进行依赖的解析、转换并设置到bean实例对应的属性中,详细的逻辑下文介绍
注解注入
除此之外通过注解修饰的属性(方法)是如何注入的?
public class MyService {
/**
* string
*/
@Value("${spring.application.name}")
private String name;
/**
* resource
*/
@Value("classpath:/init/init.sql")
private Resource res;
/**
* bean ref by parameter
*/
@Autowired
@Qualifier("myComponent1")
// or @Resource("myComponent1")
// or @Inject
private MyComponent myComponent1;
private MyComponent myComponent2;
/**
* bean ref by setter method
*/
@Autowired
public void setMyComponet2(@Qualifier("myComponent1") MyComponet component) {
this.myComponent2 = component
}
}
各注解的使用可以参考 https://www.baeldung.com/spri...
在AbstractAutowireCapableBeanFactory#applyPropertyValues之前发现还有一个动作InstantiationAwareBeanPostProcessor#postProcessProperties(是的InstantiationAwareBeanPostProcessor又出现了),在此有两个实现引起了我的注意AutowiredAnnotationBeanPostProcessor#postProcessProperties及CommonAnnotationBeanPostProcessor#postProcessProperties,我们来对比两个实现的内部逻辑
// AutowiredAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (Throwable ex) { /* handle exception */ }
return pvs;
}
// CommonAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (Throwable ex) { /* handle exception */ }
return pvs;
}
从代码及流程图可以看出,两种实现的差异仅在InjectionMetadata的查找逻辑,一个个来
AutowiredAnnotation
AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata的核心逻辑可以追踪到AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata
CommonAnnotation
CommonAnnotationBeanPostProcessor#findResourceMetadata的核心逻辑可以追踪到CommonAnnotationBeanPostProcessor.buildResourceMetadata
InjectionElement
以上,对于不同的注解不同的方式(属性/方法),会被封装为不同的InjectionElement,并最终将所有的InjectionElement封装在InjectionMetadata中
在找到InjectionElement之后,下一步便是依赖的解析和注入了(InjectionMetadata#inject)
这里的逻辑无非就是遍历内部所有的InjectionElement并执行InjectionElement.inject,上面已经介绍,对于不同的注解不同的方式(属性/方法),会被封装为不同的InjectionElement,那不同的InjectionElement也会有不同的inject逻辑,至此我们大致可以理出一个注解注入的大框架
所以,归根结底,注入的过程在AutowiredFieldElement、AutowiredMethodElement、ResourceElement、等InjectionElement内部,在继续进行之前有必要了解一下DefaultListableBeanFactory#resolveDependency
还记得上文“创建bean实体”一节中介绍参数的注入时提到的AutowireCapableBeanFactory#resolveDependency么?该函数正是调用了DefaultListableBeanFactory#resolveDependency,上文并未详细展开该函数的逻辑实现,其除了处理OptionalBean、及LazyBean之外,我们比较关心的逻辑在DefaultListableBeanFactory#doResolveDependency
该函数处理了@Value、@Qualifier、@Primary、@Order等的逻辑
@Value的解析有两个过程,1. StringValueResolver解析(${spring.sql.init} -> classpath:/init/init.sql);2. PropertyEditor转换(classpath:/init/init.sql -> Resouce);
AutowiredFieldElement无非就是使用DefaultListableBeanFactory#doResolveDependency将依赖的bean解析到,并设置到对应的属性上
AutowiredMethodElement则是使用DefaultListableBeanFactory#doResolveDependency将参数对应依赖的bean解析到,并执行对应的方法
Q: 我们是否可以自定义注解(InstantiationAwareBeanPostProcessor),来实现类似 @Value、@Autowired 的功能?
属性注入
AbstractAutowireCapableBeanFactory#applyPropertyValues
还记得在一开始提到的BeanDefinition中的propertyValues么?(xml中的bean配置、自定义的BeanDefinition,也有可能来自InstantiationAwareBeanPostProcessor#postProcessProperties),至此这一部分的属性还未注入依赖
PropertyValue中记录了需要注入的属性,已经依赖的类型(String、RuntimeBeanReference、等),根据不同的类型解析依赖的bean并设置到对应的属性上(此过程与DefaultListableBeanFactory#doResolveDependency极其相似,不再赘述)
初始化
AbstractAutowireCapableBeanFactory#initializeBean
以上,完成了bean实例的创建和属性注入,之后还有一些初始化的方法,比如各种Aware的setXxx是如何调用的、@PostConstruct是怎么调用的?
Q: Aware类有很多,除了上图中的三种之外,其他的Aware是什么时候调用的?Q: @PreDestroy是如何调用的?destroy-method是何时执行的?
Q: AbstractAdvisingBeanPostProcessor都做了什么?是如何处理AOP代理的?
注册Disposable
AbstractBeanFactory#registerDisposableBeanIfNecessary
至此,终于完成了bean实例的创建、属性注入以及之后的初始化,此后便可以开始使用了
在使用Spring的过程中经常还会碰到设置销毁逻辑的情况,如数据库连接池、线程池等等,在Spring销毁bean的时候还需要做一些处理,类似于C++中的析构
在bean的创建逻辑中,最后一个步骤则是注册bean的销毁逻辑(DisposableBean)
销毁逻辑的注册有几个条件
- 非prototype(singleton或者注册的scope)
- 非NullBean
- 指定了destroy-method(如xml中指定或者BeanDefinition中直接设置)或者存在@PreDestroy注解的方法(CommonAnnotationBeanPostProcessor.requiresDestruction)
if (!mbd.isPrototype() && requiresDestruction(bean, mbd))
满足以上条件的bean会被封装为DisposableBeanAdapter,并注册在DefaultSingletonBeanRegistry.disposableBeans中(详见附录#中间态说明)
Q: 理解了bean的销毁注册逻辑,那bean的销毁时何时触发以及如何执行的?
尝试类型转换
以上,完成了bean的创建、属性的注入、dispose逻辑的注册,但获得的bean类型与实际需要的类型可能依然不相符,在最终交付bean之前(getBean)还需要进行一次类型转换,上文反复提到过PropertyEditor,此处不例外,使用的既是PropertyEditor进行的类型转换,具体的逻辑不再赘述,再将bean转换为真正需要的类型后,便完成了整个getBean的使命
Bean销毁过程
了解了bean的完成创建过程后,那bean是如何销毁的呢?
bean的创建过程始于DefaultListableBeanFactory.getBean,销毁过程则终于ConfigurableApplicationContext#close,跟踪下去,具体的逻辑在DefaultSingletonBeanRegistry#destroySingletons
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
public void destroySingletons() {
synchronized (this.singletonObjects) {
this.singletonsCurrentlyInDestruction = true;
}
String[] disposableBeanNames;
synchronized (this.disposableBeans) {
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
}
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
// 遍历注册的DisposableBean
destroySingleton(disposableBeanNames[i]);
}
// 清理各种缓存
this.containedBeanMap.clear();
this.dependentBeanMap.clear();
this.dependenciesForBeanMap.clear();
clearSingletonCache();
}
在介绍bean创建的时候提到过两个概念
-
DefaultSingletonBeanRegistry.disposableBeans
需要注册销毁逻辑的bean会被封装为DisposableBeanAdapter并缓存在此处
-
DefaultSingletonBeanRegistry.dependentBeanMap
对于存在依赖注入关系的bean,会将bean的依赖关系缓存在此处(dependentBeanMap: 哪些bean依赖了我; dependenciesForBeanMap: 我依赖了哪些bean)
从上图中可以看出,bean的销毁顺序与创建顺序正好相反,如果有 beanA --dependsOn--> beanB --> beanC ,创建(getBean)时一定是beanC -> beanB -> beanA,销毁时一定是 beanA -> beanB -> beanC,以此避免因为依赖关系造成的一些异常情况
循环依赖
在介绍Bean创建的时候提到过earlySingletonObject,为了解决循环依赖的问题,在实例化完后属性注入之前会提前将当前的bean实体暴露出来,以防止在属性注入过程中所注入的bean又依赖当前的bean造成的类似“死锁”的状态,但即便有这样的逻辑还是要注意几点
显示设置dependsOn的循环依赖
@DependsOn("beanB")
@Component
public class BeanA {}
@DependsOn("beanC")
@Component
public class BeanB {}
@DependsOn("beanA")
@Component
public class BeanC {}
dependsOn的依赖,在bean的创建之前便会处理
Spring在实例化以上bean时,在创建BeanA之前会触发创建BeanB,创建BeanB之前会触发创建BeanC,而创建BeanC之前又会触发创建BeanA,由此引发一个无解的循环依赖
构造函数循环依赖
@Component
public class BeanA {
public BeanA(BeanB beanB) {
}
}
@Component
public class BeanB {
public BeanB(BeanC beanC) {
}
}
@Component
public class BeanC {
public BeanC(BeanA beanA) {
}
}
与dependsOn一样的原理,构造函数参数依赖,同样在bean的创建之前便会处理,从而引发无解的循环依赖
factory-method依赖
@Bean
public BeanA beanA(BeanB beanB) {
return new BeanA();
}
@Bean
public BeanB beanB(BeanC beanC) {
return new BeanB();
}
@Bean
public BeanC beanC(BeanA beanA) {
return new BeanC();
}
原理与上述相同,不再赘述
显示dependsOn、构造函数依赖、factory-method依赖任意混合
@DependsOn("beanB")
@Component
public class BeanA {
}
@Component
public class BeanB {
public BeanB(BeanC beanC) {
}
}
@Bean
public BeanC beanC(BeanA beanA) {
return new BeanC();
}
似乎我们找到了一定的规律,只要一个循环依赖中的所有bean,其依赖关系都需要在创建bean实例之前进行解决,此循环依赖则一定无解
打破无解的循环依赖
还以上述三个Bean为例,先将其中任意一个依赖设置为属性依赖(属性依赖的处理,在bean实例创建完成且暴露earlySingleton之后)
@Component
public class BeanA {
@Autowired
private BeanB beanB;
}
@Component
public class BeanB {
public BeanB(BeanC beanC) {
}
}
@Bean
public BeanC beanC(BeanA beanA) {
return new BeanC();
}
或
@DependsOn("beanB")
@Component
public class BeanA {
}
@Component
public class BeanB {
private BeanC beanC;
@Resource
public void setBeanC(BeanC beanC) {
this.beanC = beanC;
}
}
@Bean
public BeanC beanC(BeanA beanA) {
return new BeanC();
}
等等
为了避免无解的循环依赖,在构成循环依赖的一个环中,只需要保证其中至少一个bean的依赖在该bean创建且暴露earlySingleton之后处理即可
我们以“bean创建且暴露earlySingleton”为节点,在此之前处理依赖的有instance supplier parameter
、factory method parameter
、constructor parameter
、等,在此之后处理的依赖有 class property
、setter parameter
、等
小结
本文介绍了Spring体系内bean的创建及销毁过程,在经过万次的commit后,也造就了Spring一定程度上的复杂度
本文并未全方位的诠释bean的创建过程,文中遗留了很多疑问点,同时也能发现Spring提供了众多的扩展点来增强Ioc的能力,让开发者能够更好的使用/驾驭
下一篇文章,将着重介绍Spring本文中遗留的疑问点及Spring所提供的各种扩展能力
附录
中间态说明
所在类 | 属性 | 类型 | 描述 |
---|---|---|---|
SimpleAliasRegistry | aliasMap | Map<String, String> | Map from alias to canonical name.<br/> 别名alias到原始bean-name的映射 |
DefaultSingletonBeanRegistry | earlySingletonObjects | Map<String, Object> | Cache of early singleton objects: bean name to bean instance<br/> 缓存提前曝光的singleton-bean-instance,主要用于解决循环依赖,存在于该处的实例还未执行属性依赖注入及其他后处理逻辑 |
singletonFactories | Map<String, ObjectFactory> | Cache of singleton factories: bean name to ObjectFactory.<br/> 缓存用于生成、获取earlySingletonObject的工厂方法 | |
singletonObjects | Map<String, Object> | Cache of singleton objects: bean name to bean instance.<br/> 缓存已经实例化的singleton-bean-instance | |
dependentBeanMap | Map<String, Set<String>> | Map between dependent bean names: bean name to Set of dependent bean names.<br/> 如果 beanA -> beanB, beanA -> beanC,key为beanA,value为[beanB, beanC] | |
dependenciesForBeanMap | Map<String, Set<String>> | Map between depending bean names: bean name to Set of bean names for the bean's dependencies.<br/> 如果 beanB -> beanA, beanC -> beanA,key为beanA,value为[beanB, beanC] | |
singletonsCurrentlyInCreation | Set<String> | Names of beans that are currently in creation.<br/> 缓存正在创建中的singleton-bean-name | |
registeredSingletons | Set<String> | Set of registered singletons, containing the bean names in registration order.<br/> 缓存已经创建的singleton-bean-name | |
disposableBeans | Map<String, Object> | Disposable bean instances: bean name to disposable instance.<br/> 缓存注册的可销毁bean(bean的销毁逻辑) | |
FactoryBeanRegistrySupport | factoryBeanObjectCache | Map<String, Object> | Cache of singleton objects created by FactoryBeans: FactoryBean name to object.<br/> 针对singleton,缓存FactoryBean得到的真正的singleton-bean-instance |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。