源码地址:https://github.com/nieandsun/spring-study

1 简述

通过前面几篇文章,尤其下面三篇,我已经对单实例bean的创建+初始化过程做了比较详细的介绍(之后可能还会进一步细化)。
【spring】详解InitializingBean、initMethod和@PostConstruct
【bean的生命周期】— 对象创建+初始化流程分析 — 【重点@Autowired的作用时机】
【bean的生命周期】— 构造方法、@Autowired、BeanPostProcessor、InitializingBean等的执行顺序解析

在《【bean的生命周期】— 对象创建+初始化流程分析 — 【重点@Autowired的作用时机】》那篇文章里讲到在spring环境下bean的生命周期大致可以分为下面三种情况:

(1)多实例:bean的创建、初始化在bean被使用的时候,bean的销毁由JVM的垃圾回收器进行处理。
(2)单实例懒加载的bean(注意:懒加载一般指的是单实例bean):创建、初始化在bean第一次被使用的时候,之后便和单实例bean一样了,销毁在IOC容器关闭的时候。
(3)单实例bean:bean的创建、初始化在项目启动时,销毁在IOC容器关闭时。

在前面几篇文章里讲了单实例bean的创建+初始化,本篇文章将来讲一下bean的销毁,并在此基础上来验证一下上面的结论。

2 DisposableBean、destroyMethod和@PreDestroy介绍

DisposableBean、destroyMethod和@PreDestroy的作用时机在bean被销毁之前,我觉得它们三个正好可以和InitializingBean、initMethod和@PostConstruct进行一一对应:

InitializingBean—使用方法:实现InitializingBean接口,重写afterPropertiesSet方法,作用在bean初始化过程中


DisposableBean—使用方法:实现DisposableBean接口,重写destroy方法,作用在bean销毁之前

initMethod—使用方法:@Bean注入对象时,通过@Bean(initMethod = “init”)的方式,使当前bean中的init方法在bean初始化过程中被调用


destroyMethod—使用方法:@Bean注入对象时,通过@Bean(destroyMethod= “destroyMethod”)的方式,使当前bean中的destroyMethod方法在bean销毁之前被调用

@PostConstruct—JSR250定义的java规范,使用方法:直接在类中的某个方法上加上该注解,使该方法在bean初始化时被调用
@PreDestroy—JSR250定义的java规范,使用方法:直接在类中的某个方法上加上该注解,使该方法在bean销毁前被调用

3 测试

3.1 测试代码

  • 实体类 — Cat
package com.nrsc.springstudy.c074DisposableBean_destroyMothod_PreDestroy.beans;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class Cat implements InitializingBean, DisposableBean {
    private String name;

    /***
     * 构造方法-----创建对象时调用
     */
    public Cat() {
        System.out.println("Cat......constructor............");
    }

    /***
     * 设置name属性时会调用
     * @param name
     */
    public void setName(String name) {
        System.out.println("===cat=========setName========");
        this.name = name;
    }

    public String getName() {
        return name;
    }

    /***
     * 在配置类中利用注解将initMethod指向下面的init方法----对应于initMethod的用法
     */
    public void init() {
        System.out.println("Cat......init............");
    }

    /***
     * 在配置类中利用注解将destroyMethod指向下面的destroy方法----对应于destroyMethod的用法
     */
    public void destroyMethod() {
        System.err.println("cat....destroyMethod.....");
    }


    /***
     * 继承了InitializingBean接口,需要实现afterPropertiesSet方法---对应于InitializingBean的用法
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Cat......afterPropertiesSet............");
    }

    /***
     * 继承了DisposableBean接口,需要实现destroy方法---对应于DisposableBean的用法
     */
    @Override
    public void destroy() throws Exception {
        System.err.println("Cat......DisposableBean............");
    }

    @PostConstruct
    public void init2() {
        System.out.println("Cat......@PostConstruct............");
    }

    @PreDestroy
    public void destroy2() {
        System.err.println("Cat......@PreDestroy............");
    }
}

与此几乎相同的类还有三个分别为Duck、Monkey、Dog和Pig,这里不再列出。

  • 将实体类注入到IOC容器中的代码
/***
 * 单实例非懒加载的bean
 * @return
 */
@Bean(initMethod = "init", destroyMethod = "destroyMethod")
public Cat buildCat() {
    Cat cat = new Cat();
    cat.setName("花花");
    return cat;
}

/****
 * 单实例且懒加载的bean --- 使用
 * @return
 */
@Lazy
@Bean(initMethod = "init", destroyMethod = "destroyMethod")
public Duck duck() {
    Duck duck = new Duck();
    duck.setName("小黄鸭");
    return duck;
}

/***
 * 单实例且懒加载的bean --- 不使用
 * @return
 */
@Lazy
@Bean(initMethod = "init", destroyMethod = "destroyMethod")
public Monkey monkey() {
    Monkey monkey = new Monkey();
    monkey.setName("小黄鸭");
    return monkey;
}

/***
 * 多实例bean --- 但不使用
 * @return
 */
@Scope("prototype")
@Bean(initMethod = "init", destroyMethod = "destroyMethod")
public Dog dog() {
    Dog dog = new Dog();
    dog.setName("阿黄");
    return dog;
}

/***
 * 多实例bean --- 使用
 * @return
 */
@Scope("prototype")
@Bean(initMethod = "init", destroyMethod = "destroyMethod")
public Pig pig() {
    Pig pig = new Pig();
    pig.setName("八戒");
    return pig;
}
  • 容器启动代码
@Test
public void test01() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(C074Config.class);
    System.out.println("IOC容器创建完成........");
    //使用懒加载的bean -- duck
    Duck duck = (Duck) ac.getBean("duck");
    //使用多实例的bean -- pig
    Pig pig = (Pig) ac.getBean("pig");
    ac.close();
}

3.2测试结果和结论

由运行结果正好可以得到如下结论:

(1)多实例:bean的创建、初始化在bean被使用的时候,bean的销毁由JVM的垃圾回收器进行处理。
(2)单实例懒加载的bean(注意:懒加载一般指的是单实例bean):创建、初始化在bean第一次被使用的时候,之后便和单实例bean一样了,销毁在IOC容器关闭的时候。
(3)单实例bean:bean的创建、初始化在项目启动时,销毁在IOC容器关闭时。

【bean的生命周期】--- DisposableBean、destroyMethod和@PreDestroy_Spring bean的生命周期

4 简单分析一下容器关闭,bean销毁的源代码

容器关闭之前,spring会销毁所有的单实例bean,销毁逻辑主要如下:

【bean的生命周期】--- DisposableBean、destroyMethod和@PreDestroy_Spring bean的生命周期_02