Spring整理系列(06)——spring中Aware結尾介面
一、關於spring中Aware結尾介面介紹:
Spring中提供一些Aware結尾相關介面,像是BeanFactoryAware、 BeanNameAware、ApplicationContextAware、ResourceLoaderAware、ServletContextAware等等。
實現這些 Aware介面的Bean在被例項化
之後,可以取得一些相對應的資源,例如實現BeanFactoryAware的Bean在例項化後,Spring容器將會注入BeanFactory的例項,而實現ApplicationContextAware的Bean,在Bean被例項化後,將會被注入 ApplicationContext的例項等等。
通過重寫setter方法,當前bean被例項化後實現相關例項的注入。
二、以BeanNameAware、ApplicationContextAware介面舉例說明:
<bean name ="myContext" class="com.jsun.test.springDemo.aware.MyApplicationContext"></bean>
//實現BeanNameAware介面,並重寫setBeanName()方法,讓Bean獲取自己在BeanFactory配置中的名字(根據情況是id或者name)
//實現ApplicationContextAware介面,並重寫setApplicationContext()方法
public class MyApplicationContext implements BeanNameAware,ApplicationContextAware{
private String beanName;
//注入的beanName即為MyApplicationContext在BeanFactory配置中的名字(根據情況是id或者name)
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
System.out.println("MyApplicationContext beanName:" +beanName);
}
@Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
//通過重寫的介面方法,獲取spring容器例項context,進而獲取容器中相關bean資源
System.out.println(context.getBean(this.beanName).hashCode());
}
}
@Test
public void testScope(){
//單元測試再次獲取bean,並輸出bean的hashCode
System.out.println(super.getBean("myContext").hashCode());
}
上面輸出結果:
資訊: Loading XML bean definitions from URL [file:/E:/workspace/springDemo/target/classes/spring-ioc.xml]
MyApplicationContext beanName:myContext
1663216960
1663216960
八月 07, 2016 4:25:12 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
資訊: Closing org.springframework.context.support.ClassPathXmlApplicationContext@5531a519: startup date [Sun Aug 07 16:25:11 CST 2016]; root of context hierarchy
注意:除了通過實現Aware結尾介面獲取spring內建物件,也可以通過@Autowired註解直接注入相關物件,如下:
(如果需要用到靜態方法中,如工具方法,還是採用實現介面的方式)
@Autowired
private MessageSource messageSource;
@Autowired
private ResourceLoader resourceLoader;
@Autowired
private ApplicationContext applicationContext;
三、實際專案應用遇到的情況:
1、專案support作為基本的支援包,提供了獲取spring容器中bean的公共方法,該方法所屬bean被spring容器管理
1)、support專案spring-context.xml配置檔案:
<!-- 啟動自動掃描 -->
<context:component-scan base-package="com.test.spring.support"></context:component-scan>
2)、建立TestBean類,被spring容器管理,相當於業務bean:
package com.test.spring.support;
import org.springframework.stereotype.Component;
/**
* @date 建立時間:2016年8月8日 上午11:07:42
* @Description: support包中測試bean,相當於例項專案業務bean
*/
@Component("testBean")
public class TestBean {
public TestBean(){
System.out.println("TestBean 例項化。");
}
}
3)、建立SpringContextHolder類,被spring容器管理,同時,該類持有spring容器例項,提供靜態公共方法,獲取spring容器中管理的bean,如獲取TestBean的例項bean:
package com.test.spring.support;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
/**
* 以靜態變數儲存Spring ApplicationContext, 可在任何程式碼任何地方任何時候取出ApplicaitonContext.
*
*/
@Service()
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 實現ApplicationContextAware介面, 注入Context到靜態變數中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (SpringContextHolder.applicationContext != null) {
System.out.println("SpringContextHolder中的ApplicationContext被覆蓋, 原有ApplicationContext為:" + SpringContextHolder.applicationContext);
}
System.out.println("Spring容器啟動,將容器例項注入到SpringContextHolder例項bean中");
SpringContextHolder.applicationContext = applicationContext;
}
/**
* 實現DisposableBean介面,重寫destroy方法,相當於destroy-method,bean被銷燬的時候被呼叫,
* 實現在Context關閉時清理靜態變數的目的
* 令:還有InitializingBean介面,重寫afterPropertiesSet方法,相當於init-method,bean被例項化的時候被呼叫
*/
@Override
public void destroy() throws Exception {
applicationContext = null;
}
/**
* 取得儲存在靜態變數中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 從靜態變數applicationContext中取得Bean, 自動轉型為所賦值物件的型別.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
/**
* 從靜態變數applicationContext中取得Bean, 自動轉型為所賦值物件的型別.
*/
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
}
4)、建立單元測試類TestSpringContextHolder和單元測試方法,載入啟動spring容器,並通過上面第三步SpringContextHolder提供的靜態方法獲取上面第二步被spring容器管理的TestBean的例項bean:
package com.test.spring.support;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 單元測試類
*/
public class TestSpringContextHolder{
@Test
public void testGetBean(){
//獲取bean
SpringContextHolder.getBean("testBean");
}
//以下為容器例項宣告及初始化、銷燬
private ClassPathXmlApplicationContext context;
@Before
public void before(){
try {
// xml檔案用逗號或者空格符隔開,均可載入
context = new ClassPathXmlApplicationContext("spring-context.xml");
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
@After
public void after(){
context.destroy();
}
}
5)、執行單元測試,結果如下:
資訊: Loading XML bean definitions from class path resource [spring-context.xml]
Spring容器啟動,將容器例項注入到SpringContextHolder例項bean中
TestBean 例項化。
。。。ClassPathXmlApplicationContext doClose
資訊: Closing 。。ClassPathXmlApplicationContext@90f405e。。
上述結果可以得知,通過SpringContextHolder提供的靜態方法可以獲取spring容器中管理的bean物件。
2、專案biz執行過程中,需要使用support包提供的公共方法獲取當前biz專案的某個bean,作為臨時注入bean使用
1)、biz專案spring-context.xml檔案:
<!-- 啟動自動掃描,base-package 如果多個,用“,”分隔 -->
<context:component-scan base-package="com.test.spring.biz"></context:component-scan>
2)、biz專案中的業務bean:
package com.test.spring.biz;
import org.springframework.stereotype.Component;
/**
* @date 建立時間:2016年8月8日 上午11:29:53
* @Description: 業務bean
*/
@Component("bizTmpBean")
public class BizTmpBean {
public void sayHello(){
System.out.println("bizTmpBean say hello");
}
}
3)、biz專案中的公共靜態類,提供靜態方法,通過呼叫support專案靜態類SpringContextHolder提供的方法獲取biz專案中spring容器管理的bean:
package com.test.spring.biz;
import org.springframework.stereotype.Service;
import com.test.spring.support.SpringContextHolder;
/**
* @Description: biz專案中,全域性靜態類,通過support專案包提供的公共靜態方法,獲取臨時bean,呼叫臨時bean的方法
*/
@Service
public class Global {
public static void globalGetBean(){
//獲取臨時bean,呼叫臨時bean的方法
try{
BizTmpBean bizTmpBean = SpringContextHolder.getBean("bizTmpBean");
bizTmpBean.sayHello();
}catch(NullPointerException e){
System.out.println("靜態獲取臨時bean失敗");
e.printStackTrace();
}
}
}
4)、建立biz專案的單元測試類TestGlobal,用於驗證biz專案的靜態類Global是否可以臨時獲取到某個bean:
package com.test.spring.biz;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @date 建立時間:2016年8月8日 上午10:49:13
* @Description: biz專案中單元測試類
*/
public class TestGlobal {
//單元測試方法
@Test
public void testGlobal(){
//單元測試,biz專案全域性靜態類獲取臨時bean
Global.globalGetBean();
}
//以下為容器例項宣告及初始化、銷燬
private ClassPathXmlApplicationContext context;
@Before
public void before(){
try {
// xml檔案用逗號或者空格符隔開,均可載入
context = new ClassPathXmlApplicationContext("spring-context.xml");
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
@After
public void after(){
context.destroy();
}
}
5)、執行結果如下,丟擲空指標異常:
資訊: Loading XML bean definitions from class path resource [spring-context.xml]
靜態獲取臨時bean失敗
java.lang.NullPointerException
at com.test.spring.support.SpringContextHolder.getBean(SpringContextHolder.java:51)
at com.test.spring.biz.Global.globalGetBean(Global.java:15)
at com.test.spring.biz.TestGlobal.testGlobal(TestGlobal.java:18)
。。。
。。。ClassPathXmlApplicationContext doClose
資訊: Closing org.springframework.context.support.ClassPathXmlApplicationContext。。。
3、上面問題產生的原因是:support中一個bean類SpringContextHolder通過實現ApplicationContextAware介面獲取spring容器中管理的bean,但它只能從自己所在的spring容器中獲取(SpringContextHolder的bean也是被spring容器管理的),現在想通過它從biz的spring容器中獲取bean,就出現了誇spring容器的問題,臨時性bean將注入失敗。
解決的辦法,很簡單,就是讓biz的spring管理support提供公共方法的那個bean類,即讓biz專案對SpringContextHolder進行自動掃描。
biz專案spring-context.xml檔案更改:
<!-- 啟動自動掃描,新增SpringContextHolder類所在包com.test.spring.support的掃描,base-package 如果多個,用“,”分隔 -->
<context:component-scan base-package="com.test.spring.support,com.test.spring.biz"></context:component-scan>
執行結果如下:
資訊: Loading XML bean definitions from class path resource [spring-context.xml]
Spring容器啟動,將容器例項注入到SpringContextHolder例項bean中
TestBean 例項化。
bizTmpBean say hello
八月 08, 2016 2:13:01 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
資訊: Closing org.springframework.context.support.ClassPathXmlApplicationContext@484adff7: startup date [Mon Aug 08 14:13:01 CST 2016]; root of context hierarchy