Spring Bean生命周期的标准答案

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans。

什么是bean?在Spring中,被Spring ioc容器管理的实例,称作为bean。那么bean的声明周期是怎样的呢?本文带你认识Spring bean声明周期的标准答案。

 

生命周期

直接说答案太枯燥了,我们来做个比喻。

假如你现在进了饭馆,你想吃的菜厨师不会做,你这时候就得教他怎么做这道菜!那么流程是什么呢?

给后厨看菜谱--->后厨研究一下--->大厨们根据自己经验修改一下--->开始炒菜--->放佐料--->放其他佐料--->放盐--->放其他佐料--->出锅--->消灭掉

类似一下就是spring bean的生命周期:

定义--->解析--->修改定义--->实例化--->属性赋值--->初始化前置增强--->初始化--->初始化后置增强--->使用--->销毁

 

Bean定义

做菜第一步,告诉厨师怎么做!

菜谱:BeanDefinition,Bean定义,最常见方式有两种,xml方式和java注解方式。

Bean定义就是声明Spring如何进行创造对象的,如果Spring是厨师,那么Bean定义就是菜单。

一个bean定义有以下9种属性:

  1. Class :定义如何bean的class。

  2. Name:定义bean的名称

  3. Scope:定义bean的作用域

  4. Constructer Arguments:定义使用的构造方法

  5. Properties:属性

  6. Autowired Mode:注入模型

  7. Lazy initialization mode:懒加载

  8. Initialization method:初始化方法

  9. Destruction method:销毁方法

下面着重讲一下几个关键的bean定义属性

Class 

定义一个bean实例化成为哪个类。可能会有读者觉得,我平常就是写@Service @Controller @Component,bean的class就是写死的,难道还会故意把bean定义成为其他的class吗?

我们先来看一个有趣的特性。如下代码演示:

这里我们先定义Deer,也就是鹿

/**
 * This is deer.
 */
public class Deer {

  public Deer(){
    System.out.println("Dear constructs");
  }
}

然后定义Horse类,马

/**
 * This is horse.
 */
public class Horse {

  public Horse(){
    System.out.println("Horse constructs");
  }

}

下面代码可以先不用管:

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    GenericBeanDefinition definitionClass = (GenericBeanDefinition) beanFactory.getBeanDefinition("deer");
    ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
    definitionClass.setBeanClass(Horse.class);
  }
}

然后这是我们的测试代码,以下代码是构造一个spring容器,并注册一个Deer类的bean,然后把上面的那个MyBeanFactoryPostProcessor注册进去。

public class TestApp {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(Deer.class);
    context.register(MyBeanFactoryPostProcessor.class);
    context.refresh();

    System.out.println(context.getBean("deer").getClass());
  }
}

我们预期应该是添加的Deer类的bean,获取到的bean的class类应该是deer。

看下实际结果。运行程序后,打印日志如下:

class com.example.spring.bean.lifecycle.beanclass.Horse

竟然打印出的是Horse,这不是指鹿为马吗?!

再联想一下,mybatis为什么只写Mapper接口就能进行dao操作?我们知道接口是无法实例化的。

没错,mybatis就是在这点进行了扩展,通过修改mapper的bean定义中的class属性,把mapper的class修改成了一个FactoryBean,从而实现了mapper的代理类。

这个现象比较有趣,这里其实我们主要使用了BeanFactoryPostProcessor去修改了Bean定义,即使注册的bean是Deer,但是我们修改成了Horse。关于BeanFactoryPostProcessor的内容请往后看。

这就是bean定义的class属性。

 

 

Autowired Mode(注入模型)

注入模型分为四种,分别是

no:不注入,这也是默认的选项。意味着如果我们不在某个字段上标记@Autowired等标签,spring就把该字段当做是普通字段,不会进行自动注入。

byName:在容器中寻找和属性同名的bean进行注入。

byType:在容器中寻找字段的class的bean并注入。

constructor:和byType相似,只不过是在构造函数上注入。

可能说的太抽象,我们来举个例子说明下:

我们首先定义一个最最简单的Class,稍后我们把它加到spring容器中。

public class Blue {
}

然后我们声明另一个类,Green,Green里面有一个Blue属性。

public class Green {

  private Blue blue;

  public Green(){
    System.out.println("Constructs with no arg");
  }

  public Green(Blue blue){
    System.out.println("Constructs with a blue arg");
    this.blue = blue;
  }

  public Blue getBlue() {
    return blue;
  }

  public void setBlue(Blue blue) {
    this.blue = blue;
  }
}

默认情况下,我们将这两个类添加到spring容器中,Green的bean中是不会注入Blue的,因为我们没有添加@Autowired。

public class App {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(Green.class);
    context.register(Blue.class);
// context.register(AutowiringModeChangeBeanFactoryPostProcessor.class);
    context.refresh();
    Green bean = context.getBean(Green.class);
    System.out.println(bean.getBlue());
  }

}

运行程序,打印输出
 

Spring Bean生命周期的标准答案

符合我们预期。

然后我们借助实现上一个例子介绍的BeanFactoryPostProcessor,来对Green的bean定义进行一些修改,将它的注入模型改为byType。

public class AutowiringModeChangeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("green");
    beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  }
}

然后我们再来测试一下。

public class App {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(Green.class);
    context.register(Blue.class);
    context.register(AutowiringModeChangeBeanFactoryPostProcessor.class);
    context.refresh();
    Green bean = context.getBean(Green.class);
    System.out.println(bean.getBlue());
  }

}

输出结果变成了

Spring Bean生命周期的标准答案

发现Green的bean中的blue字段不为空。

之所以会实现这个效果,其实就是我们讲的,我们把Green的这个bean的注入模型改成了byType,spring就在容器中寻找blue的bean,然后注入到属性中。所以Green 中的blue不为空。

 

 

Bean扫描

以上spring的bean定义,对应了我们说的菜谱。那么下一步,厨师要理解你的菜谱了。

这一步我们成为扫描,或者解析bean定义。

什么是解析?就是扫描我们的bean定义然后添加到spring容器中。

这里我先把答案放出来。我们的bean定义最后都会放到BeanFactory的beanDefinitionMap中去。

Spring Bean生命周期的标准答案

然后我们打断点进去看一看是怎么添加进去的

Spring Bean生命周期的标准答案

查看调用栈,一步步进行添加到BeanDefinitionMap中去。xml解析同样的原理,就是把xml配置解析成bean定义。如果有需求,甚至可以自己定义txt解析去添加bean定义。解析不展开描

BeanFactoryPostProcessor

接下来,厨师可以稍微修改下你的菜谱。

在我们第一节,讲bean定义的时候,我们提到过BeanFactoryPostProcessor。

在spring容器启动时,加载我们的bean定义后,来到spring提供的第一个扩展点!那就是BeanFactoryPostProcessor。

BeanFactoryPostProcessor允许我们读取bean的配置信息,然后在spring初始化bean之前去修改bean定义。

只需要实现该接口,并添加到容器中,spring就会执行实现的方法,进行你需要的增强。

BeanFactoryPostProcessor只有一个方法需要实现 postProcessBeanFactory,在这个方法中我们可以根据传递过来的beanfactory来获取想要修改的bean定义。正如我们之前修改的注入模型和class时候做的。

Spring Bean生命周期的标准答案

 

实例化

接下来开始炒菜!

实例化意味着执行构造方法。

在refresh方法的finishBeanFactoryInitialization方法中,会对所有的单例非懒加载的bean进行创建,实例化便是其中步骤之一。

首先会先去获取bean,不存在就创建bean,解决循环依赖等。这里我们不展开描述,这不影响我们今天的专题。

如果你不知道bean是在哪个方法实例化的,那么你就打断点在构造方法上,毕竟只要实例化,无论是反射也好new也罢,都会走构造方法。

我们代码如下:

public class Green {

    private Blue blue;

    public Green(){
        System.out.println("Constructor with no arg invoked");
    }

    public Blue getBlue() {
        return blue;
    }

    public void setBlue(Blue blue) {
        this.blue = blue;
    }
}

跟踪断点:

Spring Bean生命周期的标准答案

调用栈信息如下:

Spring Bean生命周期的标准答案

Spring Bean生命周期的标准答案

也就是说Green的构造是在

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

这个方法中执行的。

注意实例化是初始化的区别,实例化仅仅是调用构造方法进行对象的创建,不包含属性的赋值等初始化方法。

 

 

属性赋值

属性赋值,我类比为放佐料。

在上一步对象构造完之后,对象内的属性还没有进行赋值。

毫无疑问,下一步就是赋值操作,例如讲Blue注入到Green里面去。

该方法就在creatBeanInstance方法的下面。

Spring Bean生命周期的标准答案

经过该步骤后,green的blue就赋值成功了,可以根据断点看。

Spring Bean生命周期的标准答案

 

具体的populate步骤,有兴趣可以自己跟下源码,我这里把获取green的那行代码放出来,小伙伴们可以自己打断点进去看看。

在org.springframework.beans.factory.support.DefaultListableBeanFactory#

doResolveDependency 方法中解析依赖,然后进入到下图的断点中:

Spring Bean生命周期的标准答案

 

值得注意的是我们之前测试注入模型的时候,设置的注入类型byType或者byName也是在populateBean里面处理的。代码如下:

 

Spring Bean生命周期的标准答案

看不懂没关系,这些个小细节先省略。我们知道这一步是进行属性赋值就行!

下面的步骤该是什么呢?属性也赋值完毕了,还要干什么?

 

 

初始化

这里我把初始化前置增强,初始化,初始化后置增强,统一归为初始化。

类似炒菜的比喻,就是放其他佐料,放盐,放其他佐料。

自定义初始化的操作,这里我们还可以进行扩展,通过实现BeanPostProcessor接口。

利用BeanPostProcessor可以自定义逻辑应用于修改bean。换句话说,也就是在bean实例化,赋值操作完毕后,允许我们通过这个接口在调用init方法之前和之后进行一些操作。

当然init方法我们也是可以定义!可以通过实现InitializingBean接口,或者在初始化方法上添加@PostConstruct注解。

BeanPostProcessor有两个方法 
postprocessBeforeInitialization()
postProcessAfterInitialization()

见名知意,分别是在init之前和之后的方法。

执行的源码如下:

Spring Bean生命周期的标准答案

 

这不就是初始化前置增强(放其他佐料),初始化(放盐),初始化后置增强(放其他佐料)!

接下来我们来验证一下。

首先我们先实现一个BeanPostProcessor,什么操作都不做,只是打印一下信息。

public class MyBeanPostProcessor implements BeanPostProcessor {

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("before initialization");
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("after initialization");
    return bean;
  }
}

然后实现InitializingBean 接口或者使用@PostConstruct注解。

public class Green {

  private Blue blue;

  @PostConstruct
  public void init(){
    System.out.println("post construct");
  }

  public Green(){
    System.out.println("Constructs with no arg");
  }

  public Green(Blue blue){
    System.out.println("Constructs with a blue arg");
    this.blue = blue;
  }

  public Blue getBlue() {
    return blue;
  }

  public void setBlue(Blue blue) {
    this.blue = blue;
  }
}

可以进行断点进行查看执行时机,篇幅原因,我这里只打印结果,感兴趣小伙伴自己断点调试效果更佳。

结果如下:

Spring Bean生命周期的标准答案

 

证实了我关于初始化的理论,before,init,after。

也就是执行

  1. BeanPostProcessor的postProcessBeforeInitialization()方法

  2. 然后执行自定义的@PostConstruct方法

  3. 然后执行postProcessAfterInitialization()方法

至此,bean的创建就结束了。

 

至于像其他什么ApplicationContextAware,BeanNameAware,没什么特别的意义,有兴趣自己打断点看一下就行了。

 

剩下就是在容器运行期间,进行bean的一些使用了。

 

销毁我不准备讲,不是重头戏!可以自己测试@PreDestroy注解,相同的套路,打断点调试!

 

最后我们来系统走一遍:

  1. bean定义

  2. bean定义被spring解析

  3. post process bean factory

  4. Constructs with no arg

  5. before initialization

  6. post construct

  7. after initialization

  8. pre destory

 

Spring Bean生命周期的标准答案

最后把代码贴出来,很简单,自己断点调试,看下控制台输出,会理解的更深!

public class Green {

  @PostConstruct
  public void init(){
    System.out.println("post construct");
  }

  @PreDestroy
  public void destroy(){
    System.out.println("pre destory");
  }

  public Green(){
    System.out.println("Constructs with no arg");
  }
}
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    System.out.println("post process bean factory");
  }
}
public class MyBeanPostProcessor implements BeanPostProcessor {

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("before initialization");
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("after initialization");
    return bean;
  }
}
public class App {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(Green.class);
    context.register(MyBeanFactoryPostProcessor.class);
    context.register(MyBeanPostProcessor.class);
    context.refresh();
    context.registerShutdownHook();
  }

}

 

总结

本文针对spring bean生命周期专题,进行从bean定义到bean销毁流程的整体讲解。推荐结合spring的官方文档和代码断点调试,来彻底理解整个生命周期,以及我们能对其做怎样的扩展。希望这篇文档能对你有一点点帮助。

 

欢迎关注技术公众号:凯哥的Java技术活

Spring Bean生命周期的标准答案

 

 

 

上一篇:Mybatis-Plus快速入门


下一篇:八月水题总结