走进Spring中Bean的世界

1,145 阅读6分钟

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

我的技术学习方法

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

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

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分别代表进入方法内部、跳到下一行、跳到下一个断点处。

  • F8按照索引目录跟到759行

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

  • F7进入doGetBean方法

    进入到这个方法后,看下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行

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

  • 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的世界,而是要走进它的世界。我在写这篇文章的时候,经常问自己这地方你懂了吗?答案模棱两可就是不懂,就需要去仔细品味源码。读源码的作用到底是什么?它会让你见识到一个顶级项目整体的架构,应用了哪些设计模式,一些问题的处理方式等等。这样在日常工作中就可以“借鉴”,让自己的代码是充满着“温度”的好代码。

相关资料