现在是时候讨论Spring Bean从产生到销毁整个过程的细节了,也就是Spring Bean的生命周期。在这里文哥先温馨提示:Spring Bean的生命周期是面试高频点之一,希望大家好好掌握哦~




spring中bean生命周期中aware回调接口是什么时候调用的_java


一. Spring Bean生命周期的概述

如果没有Spring的环境,Java Bean的生命周期非常简单,通过new关键字创建的对象就可以被使用,一旦这个对象不再被使用了(JVM中通过可达性搜索算法判断对象是否可用),这个对象就会被判定为垃圾对象,然后被垃圾回收器回收。

但是在Spring中,Bean的生命周期就不是这么简单的了。由于Spring对Bean管理灵活度非常高,这就导致Spring Bean的生命周期非常复杂。接下来,文哥带领大家一探Spring Bean生命周期的细节。为了完整地展示Spring的生命周期,文哥用一幅图来描述Spring Bean生命周期的整个过程。


spring中bean生命周期中aware回调接口是什么时候调用的_jvm_02


我们发现整个生命周期异常复杂,别慌,文哥这就给大家慢慢地分析,跟我来。

二. 详解Spring Bean的生命周期

1. BeanNameAware

这是一个接口,在Spring源码中是这样描述的。如果Spring中的Bean实现了这个接口的话,Spring的Bean的id将会传入给setBeanName的形参里面。


spring中bean生命周期中aware回调接口是什么时候调用的_生命周期_03


现在我们验证一下是不是如我们上面定义的那样,接下来我们通过几个步骤来进行分析。

1.1 第一步:定义一个类,该类必须实现BeanNameAware接口

public class User implements BeanNameAware 
{    
  public void setBeanName(String name) 
    {        
      System.out.println("bean的名称是:" + name);    
    } 
}

1.2 第二步:在Spring的配置文件中管理Bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.qf.bean.User"></bean>
</beans>

1.3 第三步:编写测试类

public class TestUser {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
}

1.4 第四步:打断点测试

我们在User的setBeanName上打上断点,查看一下效果:


spring中bean生命周期中aware回调接口是什么时候调用的_java_04


以上结果就验证了刚刚说的,这个方法里面会接收Bean的id的值。

2. BeanClassLoaderAware

在源码中,会将其描述成一个接口。


spring中bean生命周期中aware回调接口是什么时候调用的_spring_05


这个接口里面定义了setBeanClassLoader方法。这个方法就是设置Bean加载器的方法,方法的形式参数传递的是一个类加载器对象。

3. BeanFactoryAware

Spring也是将其描述成了一个接口。


spring中bean生命周期中aware回调接口是什么时候调用的_jvm_06


如果一个Bean实现了这个接口,可以获取这个Bean的Bean工厂。

4. EnvironmentAware

如果一个Bean实现了这个接口,就可以获取当前Bean对应的运行环境。


spring中bean生命周期中aware回调接口是什么时候调用的_java_07


如果我们想获取Bean对应的环境信息,我们可以这么做:

public class User implements  EnvironmentAware{
    public void setEnvironment(Environment environment) {
        System.out.println(environment);
    }
}

5. ResourceLoaderAware

这也是一个接口。


spring中bean生命周期中aware回调接口是什么时候调用的_java_08


如果一个Bean实现了这个接口我们可以获得一个资源加载器,用来去加载Bean所需要用到的资源信息。

6. ApplicationEventPublisherAware


spring中bean生命周期中aware回调接口是什么时候调用的_java_09


这个接口是用来做事件发布用的。Spring设计这个组件主要是用来组件解耦使用的,如果一个Bean所属的类实现了这个接口,就可以获取一个事件发布器。

7. MessageSourceAware


spring中bean生命周期中aware回调接口是什么时候调用的_spring_10


Spring同样将其设计成了一个接口,我们可以通过这个接口去获取Bean相关的国际化资源信息。

8. ApplicationContextAware


spring中bean生命周期中aware回调接口是什么时候调用的_生命周期_11


当一个Bean所属的类实现了这个接口之后,这个类就可以方便地获得ApplicationContext对象(Spring上下文),Spring发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该Bean的setApplicationContext(参数)方法,调用该方法时,会将容器本身ApplicationContext对象作为参数传递给该方法。

我们在Bean所属的类上面实现这个接口:

public class User implements ApplicationContextAware {
    private ApplicationContext context;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

}

我们打断点验证我们的猜想:


spring中bean生命周期中aware回调接口是什么时候调用的_spring_12


我们看看这个context是什么:


spring中bean生命周期中aware回调接口是什么时候调用的_jvm_13


这不就是我们上面说的,将ApplicationContext实例对象传入进来了吗?

9. ServletContextAware

在上面我们获取了Spring的Bean工厂实例对象,接下来Spring框架会去判断当前容器是否是一个Web类型的容器实例,如何判断?就需要调用ServletContextAware中的setServletContext方法,获取一个ServletContext容器。那到底是如何获取的呢?

private ServletContext servletContext;
@Override
public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
}

10. BeanPostProcessor

该接口我们也叫Bean的后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显式调用初始化方法的前后添加我们自己自定义的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

这两个方法是什么意思呢,给大家详细介绍:

  • postProcessBeforeInitialization:Bean实例化、依赖注入完毕之后,在调用Bean初始化之前完成一些定制的初始化任务
  • postProcessAfterInitialization:Bean实例化、依赖注入、初始化完毕时执行

现在,我们就自定义一个Bean的后置处理器,有以下几个实现步骤。

10.1 第一步:创建一个类,实现BeanPostProcessor接口

public class CustomizeBeanProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行before方法,bean是:" + bean + "bean的名称是:" + beanName);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行after方法,bean是:" + bean + "bean的名称是:" + beanName);
        return bean;
    }
}

文哥在这里特别提示:这两个方法的返回值千万不能返回为null。如果返回null那么在后续初始化方法因为通过getBean()方法获取不到Bean实例对象会报空指针异常,因为后置处理器从Spring IoC容器中取出Bean实例对象没有再次放回IoC容器中。

10.2 第二步:定义一个Pojo类

我们定义一个Student类,在里面定义一个init方法。

public class Student {
    
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    //定义一个初始化bean的时候需要执行的方法
    public void init(){
        System.out.println("初始化方法init执行了.....");
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

10.3 第三步:在Spring的配置文件中配置pojo和Bean的后置处理器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--管理student-->
    <bean id="student" class="com.qf.bean.Student" init-method="init">
        <property name="id" value="10010"></property>
        <property name="name" value="eric"></property>
        <property name="age" value="12"></property>
    </bean>
    
    <!--管理bean的后置处理器-->
    <bean class="com.qf.processor.CustomizeBeanProcessor"></bean>
</beans

10.4 第四步:测试

我们定义一个测试类,初始化一个IOC容器,然后从容器中获取Bean,然后查看控制台效果。

public class TestStudent {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
    }
}

我们查看控制台输出信息:


spring中bean生命周期中aware回调接口是什么时候调用的_jvm_14


通过验证,我们终于知道了Bean的后置处理器的作用了。

11. InitializingBean


spring中bean生命周期中aware回调接口是什么时候调用的_jvm_15


该接口只有这一个接口。这个方法将在所有的属性被初始化后调用,但是会在 init 前调用。这个接口我们一般在工作中用于工厂+策略模式优化过多的if else代码块。

现在文哥给大家演示一下,这个接口的简单用法:

11.1 第一步:在pojo类上实现这个接口。


spring中bean生命周期中aware回调接口是什么时候调用的_java_16


11.2 第二步:运行测试代码,查看控制台效果


spring中bean生命周期中aware回调接口是什么时候调用的_jvm_17


到这里我们基本上给大家把Spring Bean生命周期中所涉及的主要接口给大家讲解清楚了。接下来文哥再给大家用代码演示Spring Bean的生命周期。

三. 验证Spring Bean的生命周期

1. 定义一个POJO类

定义一个POJO类,我们分别实现上面刚刚给大家介绍的生命周期中的接口。

public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware,InitializingBean, DisposableBean {

    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    //定义一个初始化bean的时候需要执行的方法
    public void init(){
        System.out.println("初始化方法init执行了.....");
    }

    //定义销毁bean的时候需要指定的方法
    public void preDestroy(){
        System.out.println("销毁bean的方法preDestroy执行了....");
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("Student的setBeanFactory方法执行了.....");
    }

    public void setBeanName(String beanName) {
        System.out.println("Student的bean的名称是:" + beanName);
    }

    public void destroy() throws Exception {
        System.out.println("destroy方法执行了....");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet被调用了....");
    }
}

2. 定义Bean的后置处理器

这个后置处理器,在上面文哥给大家介绍过,我们需要实现里面的两个方法,注意方法的返回值一定不能为null。

public class CustomizeBeanProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行before方法,bean是:" + bean + "bean的名称是:" + beanName);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行after方法,bean是:" + bean + "bean的名称是:" + beanName);
        return bean;
    }
}

3. 定义Spring的配置文件

定义Spring的配置文件,管理我们的POJO类和后置处理器类。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--管理student-->
    <bean id="student" class="com.qf.bean.Student" init-method="init" destroy-method="preDestroy">
        <property name="id" value="10010"></property>
        <property name="name" value="eric"></property>
        <property name="age" value="12"></property>
    </bean>

    <!--管理bean的后置处理器-->
    <bean class="com.qf.processor.CustomizeBeanProcessor"></bean>
</beans>

4. 定义测试类,查看控制台输出效果

public class TestStudent {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
        //关闭容器,销毁Bean
        ((ClassPathXmlApplicationContext) context).close();
    }
}

我们此时来查看一下控制台的效果,如下图所示:


spring中bean生命周期中aware回调接口是什么时候调用的_jvm_18


通过这篇文章,给大家非常详细地介绍了Spring中Bean的生命周期,可以说本文是从源码角度,深入浅出地剖析了Spring中Bean的生命周期整个执行流程。虽然本文的讲解步骤比较繁琐,但如果大家在面试中能够回答的这么深入,相信大家一定可以让面试官刮目相看!