走进Spring中Bean的世界

以前十一假期,基于SpringBoot实现集成公司业务和通用封装的starter,有点相似支付宝的Sofa-Boot。在实现过程当中,不断优化的过程发现对源码理解很差,starter很容易写的不那么“聪明”。因此就趁着假期一点点跟着源码阅读了起来,今天来分享一篇总结简单Bean的生命周期。html

个人技术学习方法

我阅读源码的过程当中,一般分两步走:跳出来,看全景;钻进去,看本质。先了解Bean的生命周期图对其有大致了解,该技术在大脑中有了一个“简单”的构图后。再阅读相关源码去确认本身在看全景时的大脑构图是否合理。这个过程其实也适用于面对新环境,先熟悉本身所处项目的业务,再过技术栈与代码相关细节。钻进去看本质的过程每每是“煎熬的”,须要坚持与不断的思考。git

Bean的生命周期图

Bean建立过程的源码阅读索引

Spring源码版本: 5.0.9.RELEASEgithub

源码阅读索引

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类泛化体系远比此复杂多。spring

Bean的建立过程源码分析

示例代码

@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);
    }
}
复制代码

开始源码阅读

  • 按照源码阅读索引的入口,咱们查看下源码

由于这块是循环遍历全部BeanDefinitionNamesF9找到上面咱们想要看到初始化过程的Bean。下文提到的全部F7F8F9分别表明进入方法内部、跳到下一行、跳到下一个断点处。apache

  • F8按照索引目录跟到759行 设计模式

  • F7进入此方法,进入到抽象类AbstractBeanFactory 缓存

  • F7进入doGetBean方法 bash

    进入到这个方法后,看下246行,它最终会进入到DefaultSigletonBeanRegistry中的getSingleton
    到这先认识下这三个缓存Map

    • singletonObjects: singleton object cache
    • earlySingletonObjects: Cache of singleton objects exposed in advance
    • singletonFactories: singleton object factory cache

    这三级缓存具体做用咱们在populate阶段再分析架构

  • 返回到AbstractBeanFactorydoGetBean(246),F8接着跟到315行 app

    第二个参数是个函数式接口

  • F7跟进去,咱们看到到第222行调用了getObject()

  • getObject() F7跟进去其实调用的就是createBean

  • F7跟进去,到了AbstractAutowireCapableBeanFacotorycreateBean(456), 在这个方法内找到495行, F7进入此方法

    接下来才开始正式进入到建立Bean的过程

建立Bean对象,加入第三级缓存中

  • 上一步咱们进入AbstractAutowireCapableBeanFacotorydoCreateBean(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返回AbstractAutowireCapableBeanFacotorydoCreateBean(535), 而后F8到559行。
  • 接着F8跟到566行
  • F7进入此方法,到了DefaultSingletonBeanRegistry
    咱们能够发现这步是将对象建立的工厂函数缓存到singletonFactories,目的为了解决循环引用的问题,那么什么是循环引用的状况与如何解决的呢?且看下文分解。

注入Bean的属性

  • 上小节咱们提出了疑问什么是循环引用?咱们先来看一张图。
    正如咱们的示例代码,ComponetA依赖ComponentBComponentB又依赖于ComponentASpringBean的实例化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 BComponent B须要注入Component A,从缓存把Component A取出来,完成Component B的建立全过程,在将Component B返回给Component AComponent A也可完成建立全过程。
  • F8跟到populateBean(572)
    按照咱们上面的解释,这步是会去注入Componet B,而后天然而然须要建立Componet B。接下来看下是否是这样。
  • F7进入此方法,到了AbstractAutowireCapableBeanFactorypopulateBean(1277)
  • F8一直往下跟1339行打个断点,而后一直F9跳到bpAutowiredAnnotationBeanPostProcessor时候中止,F8跟到 1341行
  • F7进入此方法,来到CommonAnnotationBeanPostProcessormetadata.inject(318)
  • F7进入此方法,来到InjectionMetadatainject(81)
    写到这咱们发现须要注入的属性componetBvalA已经在checkoutedElements中,要对其进行值的注入。能够发现@Autowired和@Value值就是在这阶段对其赋值的。那么componetBvalA何时加到checkoutedElements中的呢?
  • 先回退下,找到AbstractAutowireCapableBeanFactoryapplyMergedBeanDefinitionPostPorcessors(547)行,打个断点
  • 从新启动容器,F9一直跳到ComponetA
  • F7跟进,进入到AbstractAutowireCapableBeanFactoryapplyMergedBeanDefinitionPostPorcessors(1009)
  • 1011行打个断点,而后F9一直跳到bpAutowiredAnnotationBeanPostProcessor
  • F7进入AutowiredAnnotationBeanPostProcessorpostProcessMergedBeanDefinition(231)
  • F8跟到232行,F7进入此方法,来到了AutowiredAnnotationBeanPostProcessorfindAutowiringMetadata(405)
  • F8跟到AutowiredAnnotationBeanPostProcessorbuildAutowiringMetadata(417)
  • F7进入此方法,来到了AutowiredAnnotationBeanPostProcessorbuildAutowiringMetadata(425)行,而后在433行打个断点
  • F9一直跳到ComponetB或者valA
  • F7进入此方法,来到了AutowiredAnnotationBeanPostProcessorfindAutowiredAnnotation(480)
    跟到这咱们明白了,获取到当前对象的全部field,而后找看看有没有咱们“感兴趣”的field,“感兴趣”的注解是提早定义好的,放在autowiredAnnotationTypes中。
  • autowiredAnnotationTypes中都有哪些注解,咱们找到AutowiredAnnotationBeanPostProcessor的构造方法。
    咱们能够看到“感兴趣”的注解有@Autowired@Value@Inject,因此ComponetB或者valA会最终被放置到checkedElements中。
  • 接下来咱们回到InjectionMetadatainject(81),接着分析注入的过程
    经过上面分析咱们知道elementsToIterate一共有两个属性须要注入,分别是ComponetB或者valA
  • F7进入InjectionMetadatainject(90)行,来到AutowiredAnnotationBeanPostProcessorinject(570)
  • F8跟到583行,F7进入beanFactory.resolveDependency(583)行,来到了DefaultListableBeanFacotorydoResolveDependency(1062)
  • F7进入doResolveDependency(1062),来到DefaultListableBeanFacotory的1069行,接着在1135行打个断点,F9跳到此位置
    咱们知道当前须要注入的一个属性是ComponetB是个类,因此会走这进行初始化
  • F7进入descriptor.resolveCandidate(1135)行,来到DependencyDescriptorresolveCandidate(248)
  • F7进入beanFactory.getBean(251)行,一直跟到AbstractBeanFacotorydoGetBean(239)行,如下过程都是建立Componet B的过程,因此汇集在一块儿,等Componet B中须要注入Component A时咱们在分开。
    到246行,其实就是到了上面循环依赖解决方案图中第4步先从cache取出B的实例,显然没有那么就接着按图中走,第5步没有B的示例去instantiate个示例。
    而后在317行打个断点,F9跳到这
    F7进入,F8跟到doCreateBean(495)
    F7进入,F8跟到doCreateBean(572)
    咱们如今应该很清楚的知道,咱们到了解决循环依赖问题的第7步骤populate ComponentAF7进入populateBean方法。
  • populate ComponentA时,确定最终会去试图建立ComponentA,也会回到AbstractBeanFacotrydoGetBean(239)
  • F7进入getSingleton(246)行,一直跟到DefaultSingletonBeanRegistrygetSingleton(176)行,F8跟到185行
    咱们到了解决循环依赖问题的第8步骤,从cache中取出Bean,并且这个Bean正好是咱们上文书所说的那样,是实例化后的,未完成属性注入和初始化的。
  • F8到最后doGetBean(392)
  • 接下来就是一直F8跟到AutowiredAnnotationBeanPostProcessor的inject(611)行
    这个时候就能够发现,ComponetB建立完成,注入的是预先缓存好的示例化的ComponetA
  • 接下仍然一直F8跟回到AbstractAutowireCapableBeanFacotrydoCreateBean(573)
    到这能够知道,ComponetB对于ComponetA已经注入完成,573行是执行ComponetB初始化操做,咱们主要看ComponetA的生命周期,因此能够直接F9跳事后面过程回到ComponetApopulateBean
  • F9跳回到ComponetApopulateBean
    至此Component A完全完成初始化和属性注入的过程,并且控制台没有任何输出也符合咱们生命周期图的预测。

初始化方法的前置处理

  • F7进入AbstractAutowireCapableBeanFacotrydoCreateBean(573)行来到initializeBean(1678)
  • F7进入invokeAwareMethods(1686)
    咱们的示例代码ComponetA implements BeanFactoryAware, 因此这块会回调setBeanFactory
  • F8执行完invokeAwareMethods
    符合咱们生命周期图的预期,打印了$setBeanFactory()...
  • F8跳回到initializeBean(1691)
  • F7跟进去,到了AbstractAutowireCapableBeanFacotryapplyBeanPostProcessorsBeforeInitialization(411)
    跟到416行时候能够发现两个事情,第一个是这里面一般执行的是*AwareProcessor且是在invokeAwareMethods后面执行的,符合咱们生命周期图,第二个是ApplicationContextAwareProcessor是默认给咱们全部bean都建立的*AwareProcessor,咱们来看下这个ApplicationContextAwareProcessor是干什么的。
  • F7进入applyBeanPostProcessorsBeforeInitialization(416)行,来到了ApplicationContextAwareProcessorpostProcessBeforeInitialization(79)行,F8一直跟到invokeAwareInterfaces(96)
  • F7进入invokeAwareInterfaces(96)行,来到了invokeAwareInterfaces(102)
    原来还会帮咱们默认加ApplicationContextAwareProcessor,若是咱们实现了这些接口,它就会回调。咱们的示例代码中ComponentA implements ApplicationContextAware,因此应该会执行ComponentA的setApplicationContext
  • F8跟到invokeAwareInterfaces(102)这个方法的最后,看下打印结果。
    符合咱们直接执行的*Aware接口,生命周期要早于*AwareProcessor

全部属性注入结束

  • AbstractAutowireCapableBeanFacotryinitializeBean(1678)中的invokeInitMethods(1695)行,F7进入此方法,再F8跟到invokeInitMethods(1758)
  • F8执行afterPropertiesSet,由于ComponentA implements InitializingBean,因此控制台确定会打印$afterPropertiesSet()...,以下图所示:

开始init

  • F8下面的1767行就是执行,目标init方法

建立结束

  • F8跟回到AbstractAutowireCapableBeanFacotryapplyBeanPostProcessorsAfterInitialization(1703)
    这个过程就是调用初始化的后置处理器和前置处理器过程相似。
  • F8跟到AbstractAutowireCaplableBeanFacotrydoCreateBean(614)
    Bean注册为DisposableBean会随着容器销毁而销毁。
  • F8一直跟回到DefaultSingletonBeanRegistrygetSingleton(248)
  • F7进入到此方法,看看建立Bean的最后一步
    将bean从二级缓存earlySingletonObjects转存到以及缓存singletonObjects,以上过程咱们完成了Bean的整个建立过程,Spirng之因此处理的这么“松散”(低耦合),其实就是在Bean整个生命周期过程当中,提供给用户更好的拓展能力。结合本身的业务场景,灵活定制。

Bean的销毁过程源码分析

  • 先来了解下Runtime.getRuntime().addShutdownHook(Thread hook),写段测试代码。
    运行结果:
    当main方法执行结束,至关于System.exit(0)退出,就会回调是添加的Hook ThreadSpring也是这样作的。
  • 理解Spring的钩子线程,找到SpringApplicationrun(333)行,进入refreshContext(333)行,来到SpringApplication的415行,F7进入此方法,来到AbstractApplicationContextregisterShutdownHook(930)
    这段代码与咱们的示例代码相似,这个钩子当系统退出时调用doClose()方法,这段小内容还要碎碎念个事儿,SpringBoot咱们知道他帮咱们自动装配了不少配置,其实本质是在spring.factories定义好须要自动装配的类,把定义好的类包装成Spring须要的BeanDefinition,剩下的事儿就交给Spring就行了。固然SpringBoot还有本身完整的事件发布体系(观察者模式),让整个SpringBoot的代码结构看起来很是清晰。这些话虽然有些跑题,可是这就是我为何搞SpringBoot starter却进入了Spring的世界。
  • 一个不得不说的点
    不知道你有没有想过一个问题,上面的main方法为何执行完不退出。我相信你看完这篇文章(JVM退出问题),就会找到答案。
  • 回到正题,你知道有非后台线程存在,只能使用System.exit(0)或者Runtime.getRuntime().exit(0),那么咱们稍作修改代码。
  • 在从新启动以前,咱们在AbstractApplicationContextdoClose(1017)行打个断点
  • 一直F7跟进到DefaultSingletonBeanRegistrydestroySingletons(491)行,在504行打个断点。咱们上文说到ComponentA注册为DisposableBeanF9跳到ComponetAF7进入此方法,F8跟到543行,
  • F7进入此方法,F8跟到destroyBean(571)行并F8执行

总结

整篇文章不只是想从表面了解Bean的世界,而是要走进它的世界。我在写这篇文章的时候,常常问本身这地方你懂了吗?答案模棱两可就是不懂,就须要去仔细品味源码。读源码的做用究竟是什么?它会让你见识到一个顶级项目总体的架构,应用了哪些设计模式,一些问题的处理方式等等。这样在平常工做中就能够“借鉴”,让本身的代码是充满着“温度”的好代码。

相关资料

相关文章
相关标签/搜索