Spring Bean生命周期

Stella981
• 阅读 699

1. 简介

在【Spring核心流程梳理】我们介绍了Spring容器的refresh过程,但是我们并没有进入到Spring Bean的创建等生命周期等内容。

这里,就来梳理一下Spring Bean的生命周期。

我们还是关注流程,重点关注核心的入口,忽略具体实现的细节。

目标也很简单:

  1. 知道Spring Bean相关的扩展点,方便我们做自定义和阅读源码
  2. 当遇到问题的时候,能大致了解相关的debug入口

2. Bean处理器

2.1 BeanPostProcessor

在开始之前,先简单聊两句BeanPostProcessor,因为Bean的很多处理过程涉及到它,有点俄罗斯套娃的感觉。

Object postProcessBeforeInitialization(Object bean, String beanName)
Object postProcessAfterInitialization(Object bean, String beanName)

首先要弄清楚什么是Initialization,初始化,才知道是那之前那之后,我们可以做点啥。

初始化,就是我们调用类的实例bean已经创建好了,属性也设置好了。

调用我们自定义的:InitializingBean的afterPropertiesSet,init-method方法的过程。

所以,postProcessBeforeInitialization是在bean的属性设置之后被调用。

然后,afterPropertiesSet,init-method被调用。

最后,postProcessAfterInitialization被调用

2.2 InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor继承了BeanPostProcessor,多添加了下面3个方法:

@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    return null;
}
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    return true;
}
@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
        throws BeansException {
    return null;
}

实例化Instantiation是什么过程呢,简单的来说就是new过程,Spring当然是通过反射完成。

postProcessBeforeInstantiation就是在一个bean new之前被调用。 postProcessAfterInstantiation是在bean new之后被调用。

postProcessProperties这个方法是在这个bean的property和value解析完成之后被调用。

2.3 SmartInstantiationAwareBeanPostProcessor

SmartInstantiationAwareBeanPostProcessor继承了InstantiationAwareBeanPostProcessor,又添加了3个方法:

// 探测bean的类型
Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException;

// 选择合适的构造器
Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException;

// 获得早期暴露的bean引用,Spring处理循环引用就用到这个方法
Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;

你也可以看看BeanPostProcessor与Spring无侵入扩展这篇文章。

3. AbstractAutowireCapableBeanFactory

如果Spring容器的核心是AbstractApplicationContext,那么Spring Bean相关的流程的核心就是AbstractAutowireCapableBeanFactory。

Bean生命周期最重要的流程就3个:

  1. 实例化(Instantiate):创建实例,相当于new过程
  2. 初始化(Initialize):设置值的过程,相当于setter过程
  3. 销毁:一般不需要,特殊的可以做释放资源等工作

注意:Instantiate和Initialize两个单词咋看很像,但是两个流程还是有很大差别,不要高混淆了

我们从AbstractApplicationContext的refresh方法中可以知道Bean创建的相关流程入口是:finishBeanFactoryInitialization方法。

调用链很长,分支很多,我们直接聚焦到核心的方法:AbstractAutowireCapableBeanFactory的doCreateBean方法。

3.1 doCreateBean方法

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        // 实例化流程,创建bean实例
        BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);

        // 初始化bean实例
        Object exposedObject = bean;
        //设置property value,相当于setter过程
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
        
        // 销毁相关流程
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
        return exposedObject;
    }

上面的方法是伪代码,从原来代码中抽取出核心流程的部分,避免被无关的信息所干扰。

创建bean(new)的流程入口createBeanInstance这里我们暂时不关注,我本只需要知道,现在通过反射我们获取到一个Bean的实例了。

接下来我们来看初始化的流程,首先populateBean,populate是添加输入数据的意思,根据名字我们也能猜出大概是要执行setter方法了。

3.2 populateBean方法

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // InstantiationAwareBeanPostProcessor是BeanPostProcessor的子类,这里是调用其bean实例化之后的注入接口
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                return;
            }
        }
    }

    // 后面是,获取的bean的PropertyValues,解析是否需要自动注入,检查PropertyValues有没有需要预处理等流程
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    
    // 
    if (pvs != null) {
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

3.3 initializeBean方法

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    // 检查bean是否是BeanNameAware、BeanClassLoaderAware、BeanFactoryAware调用对应方法
    invokeAwareMethods(beanName, bean);

    // 调用BeanPostProcessor的postProcessBeforeInitialization来处理bean
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    
    // 如果bean实现了InitializingBean,调用其afterPropertiesSet,如果有init-method,也会在afterPropertiesSet之后调用
    invokeInitMethods(beanName, wrappedBean, mbd);
    
    // 调用BeanPostProcessor的postProcessAfterInitialization来处理bean
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    return wrappedBean;
}

@PostConstuct并没有在initializeBean处理,而是通过CommonAnnotationBeanPostProcessor、InitDestroyAnnotationBeanPostProcessor实现。

这2个类一看就能猜到是BeanPostProcessor,事实上的确是在InitDestroyAnnotationBeanPostProcessor的postProcessBeforeInitialization方法中处理。

因为initializeBean是初始化方法,@PostConstuct是在初始化之前执行,所以,调用顺序就能确定了:

  1. @PostConstuct方法
  2. afterPropertiesSet
  3. init-method

initializeBean方法中处理的Aware是:

  1. BeanNameAware给bean设置beanName
  2. BeanClassLoaderAware给bean设置ClassLoader
  3. BeanFactoryAware给bean设置BeanFactory

其他的Aware,通过ApplicationContextAwareProcessor,它是一个BeanPostProcessor,会在bean初始化之前,但是肯定在前面3个之后调用:

  1. EnvironmentAware:设置获取环境属性相关的Enviroment
  2. EmbeddedValueResolverAware:设置SpEL解析器
  3. ResourceLoaderAware:设置资源加载器
  4. ApplicationEventPublisherAware:设置事件发布器
  5. MessageSourceAware:设置国际化相关的MessageSource
  6. ApplicationStartupAware:设置启动指标监控相关的ApplicationStartup
  7. ApplicationContextAware:设置容器本身ApplicationContext

3.4 registerDisposableBeanIfNecessary方法

这个方法咱就不细入了,简单说一下逻辑:

  1. 处理bean中有@PreDestroy注解的方法
  2. 如果实现了DisposableBean接口处理其destroy方法
  3. 检查destroy-method,如:

4. Spring Bean生命周期小结

先看一个简化流程: Spring Bean生命周期

在看一下常规流程逻辑调用链:

Spring Bean生命周期

5. 循环依赖

Spring Bean的创建过程中还有一个重要的问题,依赖循环,这里大致说2句。

public class A {
    @Resource
    private B b;
}

public class B {
    @Resource
    private A a;
}

这可咋整哦?

逻辑可以通过的DefaultSingletonBeanRegistry的三级缓存来处理:

// 缓存已经创建完成的bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 缓存ObjectFactory
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

// 缓存还未进行依赖注入和初始化方法调用的bean实例
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);


protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {
        // 如果是单例,并且允许循环引用,并且这个实例正在创建中
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            //把这个实例包装成ObjectFactory添加到三级缓存中,这样有其他引用就可以暂时获取这个未创建完成的bean
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // 设置属性等初始化过程

        return exposedObject;
    }

用个简单的流程图来大致说明一下: Spring Bean生命周期

public class A {
    public A(B b) {
    }
}

public class B {
    public B(A a) {
    }
}

Spring的Bean创建流程我们也知道,构造函数上的循环依赖是没有办法处理的,实例化都执行不了。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Spring 源码分析之 bean 实例化原理
本次主要想写springbean的实例化相关的内容。创建springbean实例是springbean生命周期的第一阶段。bean的生命周期主要有如下几个步骤:<fontcolor'red'创建bean的实例</font给实例化出来的bean填充属性初始化bea通过IOC容器使用bean
Stella981 Stella981
3年前
RequestMappingHandlerMapping 详解
我们先理简单梳理一个关系关系梳理1.springioc是spring的核心,用来管理springbean的生命周期2.MVC是一种使用MVC(ModelViewController模型视图控制器)设计创建Web应用程序的模式3.springmvc是spring的一个独立的模块,就像AOP一样在s
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Easter79 Easter79
3年前
Spring核心流程梳理
1\.简介之前其实有写过SpringBean的生命周期:Spring容器Bean与生命周期(https://my.oschina.net/u/2474629/blog/3024794)。当时太过于关注细节的实现,而进入了Spring庞大的体系之中,对于还不太了解Spring的朋友不太友好,也不方便记忆。所以,这一次以应用为
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这