SpringFramework — bean生命周期

小龙 480 2022-05-10

Bean的生命周期-初始化与销毁

个 bean 被创建的时候就到了生命周期的初始化阶段

生命周期的意义

生命周期的阶段

image-1652145052660

一个对象从被创建,到被垃圾回收,宏观上分为 5 个阶段:

  • 创建 / 实例化阶段:此时会调用类的构造方法,产生一个新的对象

  • 初始化阶段:此时对象已经创建好了,但是还未被正式使用,可能这里需要做一些额外的操作(比如预初始化数据库的连接池)

  • 运行使用期:此时对象已经完成初始化好了,程序正常运行,对象被调用

  • 销毁阶段:此时对象准备被销毁,已经不再使用,需要预先把自身占用的资源等处理好(如关闭、释放数据库连接)

  • 回收阶段:此时对象已经完成没有被引用了,被垃圾收集器回收

我们只要把握好生命周期的步骤,就可以在恰当的时机做一些恰当的处理逻辑

SpringFramework能干预的生命周期阶段

仔细观察上面的 5 个阶段,思考一个问题:作为一个框架,它能干预的是哪几个阶段呢?

  • 很明显,只有对象的回收动作不行吧。

那再思考一个问题:我们使用 SpringFramework 来获取 Bean 的前提下,又能干预哪几个阶段呢?

  • 这次 Bean 的创建应该是咱也干预不了了,只剩下初始化和销毁两个阶段可以干预了吧。

再思考,SpringFramework 如何能让我们干预 Bean 的初始化和销毁呢?

  • 回想一下 ervletervle 里面有两个方法,分别叫 initdestroy ,咱之前在使用 Servlet 开发时,没有自己调过这两个方法。但是这两个方法肯定有真实的被调用过的。这两个方法都是被 Web 容器( Tomcat 等)调用的,用来初始化和销毁 Servlet 的。这种方法的设计思想其实就是 “回调机制” ,它都不是自己设计的,而是由父类 / 接口定义好的,由第三者(框架、容器等)来调用。回调机制跟那些 Aware 接口的回调注入,在核心思想上其实是一样的。

生命周期的触发,更适合叫回调,因为生命周期方法是咱定义的,但方法被调用,是框架内部帮我们调的,那也就可以称之为 “回调” 了。

xml和注解配置初始化和销毁生命周期

xml

 <bean class="com.linkedbear.spring.lifecycle.a_initmethod.bean.Cat"
          init-method="init" destroy-method="destroy">
        <property name="name" value="mimi"/>
    </bean>

注解

@ComponentScan("com.rsthe.lifecycle")
@Component
public class DogComponent {
	@Bean(initMethod = "init", destroyMethod = "destroy")
	public Dog getDog() {
		Dog dog = new Dog();
		dog.setName("李四");
		return dog;
	}
}

初始化销毁方法的要求特征

注意一点,这些配置的初始化和销毁方法必须具有以下特征:

  • 方法访问权限去限制要求(SpringFramework底层会反射调用)

  • 方法无参数(如果设置了参数,SpringFramework也不知道传什么)

  • 方法无返回值(返回给SpringFramework也没意义)

  • 可抛出异常(异常不由自己处理,交由SpringFramework可以打断Bean的初始化 / 销毁步骤)

测试结果

测试启动类

public class InitMethodXmlApplication {
    public static void main(String[] args) throws Exception {
        System.out.println("准备初始化IOC容器。。。");
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("lifecycle/bean-initmethod.xml");
        System.out.println("IOC容器初始化完成。。。");
        System.out.println();  
        System.out.println("准备销毁IOC容器。。。");
        ctx.close();
        System.out.println("IOC容器销毁完成。。。");
    }
}

结果

准备初始化IOC容器。。。
张三 被初始化啦。。。。
IOC容器初始化完成。。。
准备销毁IOC容器。。。
张三 被销毁啦。。。。
IOC容器销毁完成。。。

可以看出在 IOC 容器初始化之前,默认情况下 Bean 已经创建好了,而且完成了初始化动作;容器调用销毁动作时,先销毁所有 Bean ,最后 IOC 容器全部销毁完成

Bean 的生命周期中,是先对属性赋值,后执行 init-method 标记的方法

JSR250规范

面的方法,都是咱手动声明注册的 Bean ,对于那些使用模式注解的 Bean ,这种方式就不好使了,因为没有可以让你声明 init-method 和 destroy-method 的地方了,@Component 注解上也只有一个 value 属性而已。这个时候咱就需要学习一种新的方式,这种方式专门配合注解式注册 Bean 以完成全注解驱动开发,那就是 JSR250 规范。

JSR250 规范中除了有 @Resource 这样的自动注入注解,还有负责生命周期的注解,包括 @PostConstruct 、@PreDestroy 两个注解,分别对应 init-method 和 destroy-method 。

JSR250 规范的执行优先级高于 init-metho / destroy-method。

InitializingBean&DisposableBean

这两个家伙实际上是两个接口,而且是 SpringFramework 内部预先定义好的两个关于生命周期的接口。他们的触发时机与上面的 init-method / destroy-method 以及 JSR250 规范的两个注解一样,都是在 Bean 的初始化和销毁阶段要回调的。下面咱演示这两个接口的使用。

三种生命周期并存

总结执行顺序才是最关键的:@PostConstructInitializingBeaninit-method

原型Bean的生命周期

对于原型 Bean 的生命周期,使用的方式跟上面是完全一致的,只是它的触发时机就不像单实例 Bean 那样了。

单实例 Bean 的生命周期是陪着 IOC 容器一起的,容器初始化,单实例 Bean 也跟着初始化(当然不绝对,后面会介绍延迟 Bean );容器销毁,单实例 Bean 也跟着销毁。原型 Bean 由于每次都是取的时候才产生一个,所以它的生命周期与 IOC 容器无关。

IOC容器初始化时原型Bean不初始化,而是在使用到时候才会创建,这个时候原型Bean的初始化动作与单实例Bean完全一致。但是在销毁的时候原型 Bean 只处理@PreDestroy 注解和 DisposableBean 接口的执行,不会没有触发 destroy-method 的执行

SpringFramework中控制Bean生命周期的三种方式

init-method & destroy-method @PostConstruct & @PreDestroy InitializingBean & DisposableBean
执行顺序 最后 最先 中间
组件耦合度 无侵入(只在 和 @Bean 中使用) 与 JSR 规范耦合 与 SpringFramework 耦合
容器支持 xml 、注解原生支持 注解原生支持,xml需开启注解驱动 xml 、注解原生支持
单实例Bean
原型Bean 只支持 init-method

# IOC # Bean的生命周期