之前十一假期,基于SpringBoot实现集成公司业务和通用封装的starter,有点类似支付宝的Sofa-Boot。在实现过程中,不断优化的过程发现对源码理解不好,starter很容易写的不那么“聪明”。所以就趁着假期一点点跟着源码阅读了起来,今天来分享一篇总结简单Bean的生命周期。
我阅读源码的过程中,通常分两步走:跳出来,看全景;钻进去,看本质。先了解Bean的生命周期图对其有大体了解,该技术在大脑中有了一个“简单”的构图后。再阅读相关源码去确认自己在看全景时的大脑构图是否合理。这个过程其实也适用于面对新环境,先熟悉自己所处项目的业务,再过技术栈与代码相关细节。钻进去看本质的过程往往是“煎熬的”,需要坚持与不断的思考。
Spring源码版本: 5.0.9.RELEASE
DefaultListableBeanFactory | preInstantiateSingletons(726) --> getBean(759); AbstractBeanFactory | getBean(198) --> doGetBean(199) | doGetBean(239) --> getSingleton(315) DefaultSingletonBeanRegistry | getSingleton(202) --> singletonFactory.getObject(222) | getSingleton(315) --> createBean(317) AbstractAutowireCapableBeanFactory | createBean(456) --> doCreateBean(495) | doCreateBean(526) --> createBeanInstance(535) return BeanWrapper | doCreateBean(526) --> addSingletonFactory(566) 加入到三级缓存SingletonFactories | doCreateBean(526) --> populateBean(572) | doCreateBean(526) --> initializeBean(573) | initializeBean(1678) --> invokeAwareMethods(1686) | invokeAwareMethods(1709) --> ((BeanNameAware) bean).setBeanName(1712) | invokeAwareMethods(1709) --> ((BeanClassLoaderAware) bean).setBeanClassLoader(1717) | invokeAwareMethods(1709) --> ((BeanFactoryAware) bean).setBeanFactory(1721) | initializeBean(1678) --> applyBeanPostProcessorsBeforeInitialization(1691) 这里面通常调用的是AwareProcessor | initializeBean(1678) --> invokeInitMethods(1695) | invokeInitMethods(1695) --> ((InitializingBean) bean).afterPropertiesSet(1758) | invokeInitMethods(1695) --> invokeCustomInitMethod(1767) | initializeBean(1678) --> applyBeanPostProcessorsAfterInitialization(1703) 复制代码
上面的类继承图是我简化过的,其实真正的Spring的BeanFacotory类泛化体系远比此复杂多。
@Slf4j @Component public class ComponentA implements InitializingBean, DisposableBean, BeanFactoryAware, ApplicationContextAware { @Autowired private ComponentB componentB; @Value("ca.test.val:default") private String valA; @Override public void destroy() { log.info("$destroy()..."); } @Override public void afterPropertiesSet() { log.info("$afterPropertiesSet()..."); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { log.info("$setApplicationContext() applicationContext:{}", applicationContext); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { log.info("$setBeanFactory() beanFactory:{}", beanFactory); } } 复制代码
@Component public class ComponentB { @Autowired private ComponentA componentA; } 复制代码
@SpringBootApplication public class EasyUcApplication { public static void main(String[] args) { /** * 使用SpringBoot-2.0.5.RELEASE去启动Spring容器,对应就是需要的Spring-5.0.9.RELEASE。 */ SpringApplication.run(EasyUcApplication.class, args); } } 复制代码
因为这块是循环遍历所有 BeanDefinitionNames
, F9
找到上面我们想要看到初始化过程的Bean。下文提到的所有 F7
、 F8
、 F9
分别代表进入方法内部、跳到下一行、跳到下一个断点处。
F8
按照索引目录跟到759行
F7
进入此方法,进入到抽象类 AbstractBeanFactory
F7
进入doGetBean方法
DefaultSigletonBeanRegistry
中的 getSingleton
到这先认识下这三个缓存 Map
:
这三级缓存具体作用我们在 populate
阶段再分析
返回到 AbstractBeanFactory
的 doGetBean(246)
, F8
接着跟到315行
F7
跟进去,我们看到到第222行调用了 getObject()
getObject()
F7
跟进去其实调用的就是 createBean
F7
跟进去,到了 AbstractAutowireCapableBeanFacotory
的 createBean(456)
, 在这个方法内找到495行, F7
进入此方法
AbstractAutowireCapableBeanFacotory
的 doCreateBean(526)
行, F8
一直到535行, F7
进入此方法。 protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { /*省略代码*/ // 调用有Autowire的构造方式: 就是说构造方法的成员变量是需要注入值的。 Class<?> beanClass = resolveBeanClass(mbd, beanName); Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 简单的无参构造器:我们这里会走到这种方式 return instantiateBean(beanName, mbd); //1128 } 复制代码
F7
进入到上面的 instantiateBean(1128)
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { /*省略代码*/ // 1.实例化对象 Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); // 2.创建该对象的BeanWrapper BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } 复制代码
上面出现个BeanWrapper,它的作用是干什么的呢?
setPropertyValue
去进行赋值, BeanWrapper
提供一种可以直接对属性赋值的能力。如下面所示: Class<?> clazz = Class.forName("cn.coderjia.easyuc.component.ComponentA"); Constructor<?> ctor = clazz.getDeclaredConstructor(); Object bean = ctor.newInstance(); BeanWrapper beanWrapper = new BeanWrapperImpl(bean); beanWrapper.setPropertyValue("valA", "val"); 复制代码
F8
返回 AbstractAutowireCapableBeanFacotory
的 doCreateBean(535)
, 然后 F8
到559行。
F8
跟到566行
F7
进入此方法,到了DefaultSingletonBeanRegistry
我们可以发现这步是将对象创建的工厂函数缓存到 singletonFactories
,目的为了解决循环引用的问题,那么什么是循环引用的情况与如何解决的呢?且看下文分解。 ComponetA
依赖 ComponentB
, ComponentB
又依赖于 ComponentA
, Spring
中 Bean
的实例化 Bean
对象后,会对属性值注入( populate
),这样就会出现如上图的循环调用过程( instantiate A
-> populate ComponentB
-> instantiateB
-> populate ComponentA
-> instantiate A
)。 Bean
对象工厂放置到第三级缓存中。 F7
进入到getEarlyBeanReference(566)
理论上讲,两级缓存就够了,对象实例化后放到 earlySingletonObjects
缓存中,在需要提前引用的地方取出就可以解决循环引用的问题。但是第三级缓存存在的意义除了保存 Bean
外,还可以在获取的时候,对 Bean
可以做前置处理( InstantiationAwareBeanPostProcessor
),有这样一种拓展的能力。
简单的理解就如上面图中的过程: 创建 Component A
后,把 Component A
缓存起来,注入 Component B
属性时,创建 Component B
, Component B
需要注入 Component A
,从缓存把 Component A
取出来,完成 Component B
的创建全过程,在将 Component B
返回给 Component A
, Component A
也可完成创建全过程。 F8
跟到 populateBean(572)
按照我们上面的解释,这步是会去注入 Componet B
,然后自然而然需要创建 Componet B
。接下来看下是不是这样。 F7
进入此方法,到了 AbstractAutowireCapableBeanFactory
的 populateBean(1277)
行
F8
一直往下跟1339行打个断点,然后一直 F9
跳到 bp
为 AutowiredAnnotationBeanPostProcessor
时候停止, F8
跟到 1341行
F7
进入此方法,来到 CommonAnnotationBeanPostProcessor
的 metadata.inject(318)
行
F7
进入此方法,来到 InjectionMetadata
的 inject(81)
写到这我们发现需要注入的属性 componetB
和 valA
已经在 checkoutedElements
中,要对其进行值的注入。可以发现@Autowired和@Value值就是在这阶段对其赋值的。那么 componetB
和 valA
什么时候加到 checkoutedElements
中的呢? AbstractAutowireCapableBeanFactory
的 applyMergedBeanDefinitionPostPorcessors(547)
行,打个断点
F9
一直跳到 ComponetA
F7
跟进,进入到 AbstractAutowireCapableBeanFactory
的 applyMergedBeanDefinitionPostPorcessors(1009)
行
1011
行打个断点,然后 F9
一直跳到 bp
为 AutowiredAnnotationBeanPostProcessor
F7
进入 AutowiredAnnotationBeanPostProcessor
的 postProcessMergedBeanDefinition(231)
行
F8
跟到232行, F7
进入此方法,来到了 AutowiredAnnotationBeanPostProcessor
的 findAutowiringMetadata(405)
行
F8
跟到 AutowiredAnnotationBeanPostProcessor
的 buildAutowiringMetadata(417)
行
F7
进入此方法,来到了 AutowiredAnnotationBeanPostProcessor
的 buildAutowiringMetadata(425)
行,然后在433行打个断点
F9
一直跳到 ComponetB
或者 valA
F7
进入此方法,来到了 AutowiredAnnotationBeanPostProcessor
的 findAutowiredAnnotation(480)
行
跟到这我们明白了,获取到当前对象的所有 field
,然后找看看有没有我们“感兴趣”的 field
,“感兴趣”的注解是提前定义好的 ,放在autowiredAnnotationTypes
中。 autowiredAnnotationTypes
中都有哪些注解,我们找到 AutowiredAnnotationBeanPostProcessor
的构造方法。
我们可以看到“感兴趣”的注解有 @Autowired
、 @Value
、 @Inject
,所以 ComponetB
或者 valA
会最终被放置到 checkedElements
中。 InjectionMetadata
的 inject(81)
,接着分析注入的过程
通过上面分析我们知道elementsToIterate一共有两个属性需要注入,分别是 ComponetB
或者 valA
F7
进入 InjectionMetadata
的 inject(90)
行,来到 AutowiredAnnotationBeanPostProcessor
的 inject(570)
行
F8
跟到583行, F7
进入 beanFactory.resolveDependency(583)
行,来到了 DefaultListableBeanFacotory
的 doResolveDependency(1062)
行
F7
进入 doResolveDependency(1062)
,来到 DefaultListableBeanFacotory
的1069行,接着在1135行打个断点, F9
跳到此位置
我们知道当前需要注入的一个属性是ComponetB是个类,所以会走这进行初始化 F7
进入 descriptor.resolveCandidate(1135)
行,来到 DependencyDescriptor
的 resolveCandidate(248)
行
F7
进入 beanFactory.getBean(251)
行,一直跟到 AbstractBeanFacotory
的 doGetBean(239)
行,以下过程都是创建 Componet B
的过程,所以聚集在一起,等 Componet B
中需要注入 Component A
时我们在分开。 F9
跳到这
F7
进入, F8
跟到 doCreateBean(495)
行
F7
进入, F8
跟到 doCreateBean(572)
行
我们现在应该很清楚的知道,我们到了解决循环依赖问题的第7步骤 populate ComponentA
, F7
进入 populateBean
方法。 populate ComponentA
时,肯定最终会去试图创建 ComponentA
,也会回到 AbstractBeanFacotry
的 doGetBean(239)
行
F7
进入 getSingleton(246)
行,一直跟到 DefaultSingletonBeanRegistry
的 getSingleton(176)
行, F8
跟到185行
我们到了解决循环依赖问题的第8步骤,从 cache
中取出 Bean
,而且这个 Bean
正好是我们上文书所说的那样,是实例化后的,未完成属性注入和初始化的。 F8
到最后 doGetBean(392)
行
F8
跟到 AutowiredAnnotationBeanPostProcessor
的inject(611)行
这个时候就可以发现, ComponetB
创建完成,注入的是预先缓存好的示例化的 ComponetA
F8
跟回到 AbstractAutowireCapableBeanFacotry
的 doCreateBean(573)
行
到这可以知道, ComponetB
对于 ComponetA
已经注入完成,573行是执行 ComponetB
初始化操作,我们主要看 ComponetA
的生命周期,所以可以直接 F9
跳过后面过程回到 ComponetA
的 populateBean
F9
跳回到 ComponetA
的 populateBean
至此 Component A
彻底完成初始化和属性注入的过程,而且控制台没有任何输出也符合我们生命周期图的预测。 F7
进入 AbstractAutowireCapableBeanFacotry
的 doCreateBean(573)
行来到 initializeBean(1678)
行
F7
进入 invokeAwareMethods(1686)
行
我们的示例代码 ComponetA implements BeanFactoryAware
, 所以这块会回调 setBeanFactory
F8
执行完 invokeAwareMethods
符合我们生命周期图的预期,打印了 $setBeanFactory()...
F8
跳回到 initializeBean(1691)
行
F7
跟进去,到了 AbstractAutowireCapableBeanFacotry
的 applyBeanPostProcessorsBeforeInitialization(411)
行
跟到416行时候可以发现两个事情,第一个是这里面通常执行的是 *AwareProcessor
且是在 invokeAwareMethods
后面执行的,符合我们生命周期图,第二个是 ApplicationContextAwareProcessor
是默认给我们所有bean都创建的 *AwareProcessor
,我们来看下这个 ApplicationContextAwareProcessor
是干什么的。 F7
进入 applyBeanPostProcessorsBeforeInitialization(416)
行,来到了 ApplicationContextAwareProcessor
的 postProcessBeforeInitialization(79)
行, F8
一直跟到 invokeAwareInterfaces(96)
行
F7
进入 invokeAwareInterfaces(96)
行,来到了 invokeAwareInterfaces(102)
行
原来还会帮我们默认加 ApplicationContextAwareProcessor
,如果我们实现了这些接口,它就会回调。我们的示例代码中 ComponentA implements ApplicationContextAware
,所以应该会执行ComponentA的 setApplicationContext
F8
跟到 invokeAwareInterfaces(102)
这个方法的最后,看下打印结果。
符合我们直接执行的 *Aware
接口,生命周期要早于 *AwareProcessor
。 AbstractAutowireCapableBeanFacotry
的 initializeBean(1678)
中的 invokeInitMethods(1695)
行, F7
进入此方法,再 F8
跟到 invokeInitMethods(1758)
行
F8
执行 afterPropertiesSet
,因为 ComponentA implements InitializingBean
,所以控制台肯定会打印 $afterPropertiesSet()...
,如下图所示:
F8
下面的1767行就是执行,目标init方法
F8
跟回到 AbstractAutowireCapableBeanFacotry
的 applyBeanPostProcessorsAfterInitialization(1703)
行
这个过程就是调用初始化的后置处理器和前置处理器过程类似。 F8
跟到 AbstractAutowireCaplableBeanFacotry
的 doCreateBean(614)
行
Bean
注册为 DisposableBean
会随着 容器
销毁而销毁。 F8
一直跟回到 DefaultSingletonBeanRegistry
的 getSingleton(248)
行
F7
进入到此方法,看看创建Bean的最后一步
将bean从二级缓存 earlySingletonObjects
转存到以及缓存 singletonObjects
,以上过程我们完成了 Bean
的整个创建过程, Spirng
之所以处理的这么“松散”(低耦合),其实就是在 Bean
整个生命周期过程中,提供给用户更好的拓展能力。结合自己的业务场景,灵活定制。 Runtime.getRuntime().addShutdownHook(Thread hook)
,写段测试代码。
运行结果:
当main方法执行结束,相当于 System.exit(0)
退出,就会回调是添加的 Hook Thread
, Spring
也是这样做的。 Spring
的钩子线程,找到 SpringApplication
的 run(333)
行,进入 refreshContext(333)
行,来到 SpringApplication
的415行, F7
进入此方法,来到 AbstractApplicationContext
的 registerShutdownHook(930)
行
这段代码与我们的示例代码类似,这个 钩子
当系统退出时调用 doClose()
方法,这段小内容还要碎碎念个事儿, SpringBoot
我们知道他帮我们自动装配了很多配置,其实本质是在 spring.factories
定义好需要自动装配的类,把定义好的类包装成 Spring
需要的 BeanDefinition
,剩下的事儿就交给 Spring
就好了。当然 SpringBoot
还有自己完整的事件发布体系( 观察者模式
),让整个 SpringBoot
的代码结构看起来非常清晰。这些话虽然有些跑题,但是这就是我为什么搞 SpringBoot starter
却进入了 Spring
的世界。
后台线程
存在,只能使用 System.exit(0)
或者 Runtime.getRuntime().exit(0)
,那么我们稍做修改代码。
AbstractApplicationContext
的 doClose(1017)
行打个断点
F7
跟进到 DefaultSingletonBeanRegistry
的 destroySingletons(491)
行,在504行打个断点。我们上文说到 ComponentA
注册为 DisposableBean
, F9
跳到 ComponetA
。 F7
进入此方法, F8
跟到543行,
F7
进入此方法, F8
跟到 destroyBean(571)
行并 F8
执行
整篇文章不仅是想从表面了解 Bean
的世界,而是要走进它的世界。我在写这篇文章的时候,经常问自己这地方你懂了吗?答案模棱两可就是不懂,就需要去仔细品味源码。读源码的作用到底是什么?它会让你见识到一个顶级项目整体的架构,应用了哪些设计模式,一些问题的处理方式等等。这样在日常工作中就可以“借鉴”,让自己的代码是充满着“温度”的好代码。