文章目錄
- 前言
- 一、Spring 容器警告產生的場景
- 二、Spring 容器未關閉后果分析
- 2.1、肉眼可見的警告
- 2.2、導致的內存泄漏
- 2.2.1、什么是內存泄漏?
- 2.2.2、如何判斷內存泄漏?
- 2.2.3、Java 中的 GC(垃圾回收)
- 2.2.4、Java 中會導致內存泄漏的情況
- 2.2.5、Spring 容器未關閉導致的內存泄漏問題
- 三、如何手動關閉 Spring 容器(3.2 最常用)
- 3.1、context.close();
- 3.2、((ConfigurableApplicationContext) context).close();
- 3.2.1、導入 org.springframework.context.support.AbstractApplicationContext 包
- 3.2.2、刪掉多余的導包
- 3.3、使用獲取對象公開聲明的方法
- 3.3.1、Method Class.getMethod(String name, Class<?>... parameterTypes)
- 3.3.2、如何使用該方法關閉 Spring 容器
- 總結
前言
我們在初始化了 Spring IoC 的容器 ApplicationContext,并加載完配置文件之后,如果不對容器進行處理,首先我們直觀上看到的就是 IDE 的警告:Resource leak: 'context' is never closed。其次還有什么其他層次的問題?這類問題我們如何去解決?本文就這類問題提出了三種不同的解決方式,讓你通過一個問題解決一類問題。一、Spring 容器警告產生的場景
我們初始化了 Spring IoC 的容器 ApplicationContext,并加載完配置文件,創建了一個 Bean 的實例,代碼如下:
public class Test {public static void main(String[] args) {// 初始化Spring容器applicationContext,加載配置文件ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 通過容器獲取test實例TestDao dao = (TestDao) context.getBean("test");// test為配置文件中的iddao.sayHello();}
}
可以看得到這里我們在使用完容器之后并沒有對容器進行處理,然后 IDE 就發出了如下警告:
Resource leak: 'context' is never closed
提示我們:容器沒有關閉,警告內容具體如下圖所示:
二、Spring 容器未關閉后果分析
2.1、肉眼可見的警告
對于強迫癥來說,這不是要了老命嗎?我好好的一個項目你給我來個感嘆號?不行我一定要解決!
使用快捷鍵快速定位光標行出現的問題,根據提示添加如下代碼,什么意思呢?忽略警告。如果你僅僅就是為了去掉警告,你就不必繼續往下看了。這個方式完全可以滿足你。
@SuppressWarnings("resource")
2.2、導致的內存泄漏
容器未關閉可能會導致內存泄漏,說到這里可能會有人有疑問:
Java 不是有 GC(垃圾回收)機制嗎?怎么會導致內存泄漏呢?別急,我們來一步一步分析。
2.2.1、什么是內存泄漏?
內存泄漏是指不再被使用的對象或變量一直占據在內存中。
2.2.2、如何判斷內存泄漏?
檢查 Java 中的內存泄漏,一定要將程序各個分支情況都完成執行至結束,然后看其是否被使用過,如果沒有才能判定這個對象屬于內存泄漏。
2.2.3、Java 中的 GC(垃圾回收)
Java 虛擬機 JVM 會將不再使用的對象或變量從內存中回收來釋放內存。
(關于 Java 中 GC 的內容這里不做贅述,可以移步我的相關 Java 專欄查看)
2.2.4、Java 中會導致內存泄漏的情況
- 當長生命周期的對象持有短生命周期的對象的引用,就很可能發生內存泄漏。盡管短生命周期的對象已經不再需要,但是長生命周期的對象一直持有它的引用導致其無法被回收。例如,緩存系統;加載一個對象放在緩存系統中,一直不去使用這個對象,但是它一直被緩存引用,所以不會被回收導致緩存泄漏。
- 當一個對象被存儲進 HashSet 集合中,就不可修改這個對象中用于計算哈希值的屬性了。否則,對象修改后的哈希值與剛添加進 HashSet 集合時的哈希值不一樣,此時如果將當前對象的引用作為參數,用 contains 方法判斷對象是否存在,則會返回找不到對象的結果。這會導致無法從 HashSet 單獨刪除當前對象,造成內存泄漏。
2.2.5、Spring 容器未關閉導致的內存泄漏問題
Spring IoC 容器在我們開啟之后,JVM 無法像回收對象或者變量的那種來進行回收。Spring 容器的生命周期是比較長的,因為它用于管理所有初始化的 Bean,其生命周期在 Bean 之后(具體關于 Spring 的生命周期我們后面會講到),如果我們不及時關閉它,就會占用內存導致 JVM 效率降低同時造成內存泄漏。當然,這也不符合我們的開發規范。
三、如何手動關閉 Spring 容器(3.2 最常用)
我們該如何解決關閉容器、流的一類問題呢?下面整理了 3 種方法,第一種最為方便,第二種是我們開發中最常使用的方法,第三種是最為簡單粗暴的方法,大家可以根據自己需求來使用。
3.1、context.close();
處理 Spring 容器類似于 Scanner 流,我們按照關閉 Scanner 流的思路,打點調用 close() 方法,添加關閉代碼如下所示:
context.close();
這時仍然還是報錯。根據提示:The method close() is undefined for the type ApplicationContext
,我們會得知 close() 方法并未直接定義在 Spring IoC 容器中,使用快捷鍵快速定位光標行出現的問題,我們對 context 進添加類型轉換,如下圖所示:
這個時候就添加了如下一行代碼:
((AbstractApplicationContext) context).close();
這樣是可以關閉掉 Spring 容器。其解決的就是context.close();
的問題。
3.2、((ConfigurableApplicationContext) context).close();
在 Spring 中定義了關閉掉 Spring 容器的方法 close(),該方法定義在 ApplicationContext 的子類 ConfigurableApplicationContext 中。那我們該如何快速調出它關閉容器呢?
3.2.1、導入 org.springframework.context.support.AbstractApplicationContext 包
我們使用快捷鍵先進行 3.1 的步驟,然后刪掉 3.1 的關閉代碼((AbstractApplicationContext) context).close();
,重寫一次關閉代碼context.close();
,這個時候我們就可以看到強轉的時候多了一個類型 ConfigurableApplicationContext,我們選擇這個即可,如下圖所示:
注意:一定要導入 org.springframework.context.support.AbstractApplicationContext 包才會出現 ConfigurableApplicationContext 的強轉類型。
這個時候我們的關閉代碼就是下面這樣的:
((ConfigurableApplicationContext) context).close();
3.2.2、刪掉多余的導包
這個時候我們就可以根據提示將多余的導包刪掉了,包括上面的 org.springframework.context.support.AbstractApplicationContext。
小結:這個寫法是我們在開發中最常用的手動關閉 Spring 容器的方法。
3.3、使用獲取對象公開聲明的方法
3.3.1、Method Class.getMethod(String name, Class<?>… parameterTypes)
補充的這個方法的作用是獲得對象所聲明的公開方法,這也是我們在開發中獲取對象方法的常用方法:
Method Class.getMethod(String name, Class<?>... parameterTypes)
參數說明:
- 參數 name 獲得當前方法的名字。
- 參數 parameterTypes 是按聲明順序標識該方法形參類型。
- 如果對象內的方法的形參是 int 類型的,則 parameterTypes 是 int.class。
舉例如下:
person.getClass().getMethod("Speak", null);
//獲得person對象的Speak方法,因為Speak方法沒有形參,所以parameterTypes為nullperson.getClass().getMethod("run", String.class);
//獲得person對象的run方法,因為run方法的形參是String類型的,所以parameterTypes為String.class
3.3.2、如何使用該方法關閉 Spring 容器
根據 3.3.1 中的內容,我們可以通過獲取 context 對象的方法 close() 并 invoke 掉 context 容器對象(null 值省略),代碼如下:
context.getClass().getMethod("close").invoke(context);
但是需要注意,如果使用這個方法,就需要對異常進行處理,我這里對異常進行捕獲,完整代碼如下:
package cn.bailu.ch1.test;import java.lang.reflect.InvocationTargetException;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.bailu.ch1.dao.TestDao;public class Test {public static void main(String[] args) {// 初始化Spring容器applicationContext,加載配置文件ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 通過容器獲取test實例TestDao dao = (TestDao) context.getBean("test");// test為配置文件中的idtry {context.getClass().getMethod("close").invoke(context);} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException| SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();}dao.sayHello();}
}
總結
本文就如何關閉 Spring IoC 容器給大家帶來了三種常見的解決方式,其中第一種方式是最為簡單的,第二種方式是我們在開發中最為常用的,這個方式很大程度上考察了你對于 Spring 源碼的了解程度,你了解源碼才能知道里面的方法,而第三種方式是最為簡單粗暴的,同時也是我們在獲取對象其他方法時較為常用的,這個方法考察的就是你對于 Java 基本代碼的了解程度,對于使用就根據你自己的需求來了。一個簡單的案例足見你的基本功,扎實基礎,多看源碼!我是白鹿,一個不懈奮斗的程序猿。望本文能對你有所裨益,歡迎大家的一鍵三連!若有其他問題、建議或者補充可以留言在文章下方,感謝大家的支持!