Spring学习笔记:Bean的销毁

本文是自己学习的一个总结,和该文章对应的是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回收的,并且回收是在容器关闭以后。

猜你喜欢

转载自blog.csdn.net/sinat_38393872/article/details/107052305