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回收的,並且回收是在容器關閉以後。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章