Spring学习笔记:自定义Bean的销毁

    技术2022-07-10  102

    本文是自己学习的一个总结,和该文章对应的是bean的初始化这篇文章,链接如下https://blog.csdn.net/sinat_38393872/article/details/106996679


    1、bean的销毁简介

    1.1、bean的初始化发生在什么阶段

    Bean的销毁一般发生在容器关闭的阶段。我们可以在销毁时定制一些动作满足需求


    2、Bean销毁的回调函数

    2.1、基于@PreDestroy,销毁前回调函数

    2.1.1、使用@PreDestroy

    @PreDestroy,从名字上看就能知道,是bean销毁前的回调函数。该注解的使用方式是在被定义成Bean的类A中实现一个方法,并用@PreDestroy标注这个方法,那么这个方法就是类A作为Bean在销毁前的回调方法。类A作为bean在销毁前,会调用被@PreDestroy标注的方法。

    我们看看例子。 DefaultUserFactory的实现如下,这是我们要注册为Bean的类。

    @Component public class DefaultUserFactory { @PreDestroy public void preDestroy() { System.out.println("@PreDestroy:DefaultUserFactory销毁中"); } }

    生成容器,并注册DefaultUserFactory,随后立即销毁,我们看系统在DefaultUserFactory销毁前会不会调用preDestroy()函数。

    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DefaultUserFactory.class); applicationContext.refresh(); applicationContext.close();

    最后打印结果如下,回调函数成功调用。

    2.1.2、同一个类中使用多个@PreDestroy

    和bean初始化的@PostConstruct一样,@PreDestroy在类中使用标注方法,当这个类被初始化成bean之后就会调用@PreDestroy标注的方法。

    其中,@PreDestroy可以在类中标注多个方法,并且类被初始化成bean之后,所有被@PreDestroy标注的方法都会被回调,但是调用的顺序不能保证,并不是按定义顺序调用的,系统似乎有自己的一套规则。

    2.1.3、注解生效范围

    @PreDestroy在类A 中使用,那只有类A是通过注解的方式初始化成bean时,@PreDestroy才会生效。

    但是如果类A是通过XML初始化成bean,那@PreDestroy就不会起作用。

    2.2、实现DisposableBean覆写destroy

    若类A要被注册为bean,那可令类A实现DisposableBean接口,覆写其中的destroy方法。这样A作为bean在销毁过程中会回调覆写的destroy方法。

    基于上面的代码,我们在DefaultUserFactory中加入destroy相关的代码

    @Component public class DefaultUserFactory implements DisposableBean { @PreDestroy public void preDestroy() { System.out.println("@PreDestroy:DefaultUserFactory销毁中"); } @Override public void destroy() throws Exception { System.out.println("destroy:DefaultUserFactory销毁中"); } }

    其余代码不变

    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DefaultUserFactory.class); applicationContext.refresh(); applicationContext.close();

    最后输出结果如下

    ### 2.2.1、destroy的生效范围 实现DisposableBean覆写destroy,这个方法与@PreDestroy不同,无论是通过注解还是通过xml,destroy都可以生效。 ## 2.3、基于@Bean的destroyMethod属性,初始化后回调函数 @Bean中有个属性是destroyMethod,他是用来将类中的某个方法指定为销毁函数。我们看看例子。

    接着上面的代码,我们在DefaultUserFactory中加入@Bean相关的代码

    @Component public class DefaultUserFactory implements DisposableBean { @PreDestroy public void preDestroy() { System.out.println("@PreDestroy:DefaultUserFactory销毁中"); } @Override public void destroy() throws Exception { System.out.println("destroy:DefaultUserFactory销毁中"); } @Bean(destroyMethod = "doDestroy") public DefaultUserFactory getDefaultUserFactory() { return new DefaultUserFactory(); } public void doDestroy() { System.out.println("@Bean.destroyMethod:DefaultUserFactory销毁中"); } }

    其余代码不变,

    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DefaultUserFactory.class); applicationContext.refresh(); applicationContext.close();

    可以预测,最后生成的容器中有两个类型为DefaultUserFactory的Bean。一个是容器生成时注册进去的bean,这个bean只设置了@PreDestroy和destroy的回调函数;另一个是基于@Bean生成的bean,这个类不仅有@PreDestroy和destroy的回调函数,还有destroyMethod的回调函数。

    最后输出结果如下

    2.3.1、destroyMethod的生效范围

    因为这也是基于注解实现的,所以只有容器是通过注解生成时destroyMethod才有效,通过xml生成容器时destroyMethod会无效。

    2.4、@PreDestroy,destroy和destroyMethod的执行顺序

    bean的销毁和bean的初始化很像,两者的实现方式也都是注解,实现接口,@Bean属性三种方式。而这三种实现方式的执行顺序在bean的销毁和初始化的执行顺序也一样。

    bean的初始化又分为构造->属性填充->初始化,而以上三种方式就对应着这三个阶段。

    @PostConstruct在构造结束后会被调用,afterPropertiesSet在属性填充后会被调用,initMethod在初始化完成后会被调用。

    所以执行顺序是@PreDestroy -> destroy -> destroyMethod


    3、java GC 什么时候回收spring容器管理的bean对象

    在Spring容器关闭了以后,Java GC会对容器中的bean进行回收。当然,就GC的特性而言,基本上不可能容器一关闭其中的bean对象就会被回收,GC的处理是随机的。

    我们可以用下面的代码来验证这一观点。我们继续沿用上面的代码,然后令DefaultUserFactory复写finalize方法。

    @Override protected void finalize() throws Throwable { System.out.println("bean对象正在被回收"); }

    之后在容器关闭以后强制执行GC操作,并且GC操作以后沉睡一会。之所以要沉睡一会是因为GC的操作是随机的,不是立刻执行的,为了防止GC还没运行程序就直接退出看不到效果才在GC回收语句之后沉睡一会。

    public static void main(String[] args) throws Exception{ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(DefaultUserFactory.class); applicationContext.refresh(); applicationContext.close(); System.gc(); Thread.sleep(50000L); }

    输出结果如下 我们可以看到,Spring容器中的bean对象是可以被GC回收的,并且回收是在容器关闭以后。

    Processed: 0.010, SQL: 9