問題背景
在我們的web程序中空扎,用spring來管理各個實例(bean), 有時在程序中為了使用已被實例化的bean, 通常會用到這樣的代碼:
ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext-common.xml");
AbcService abcService = (AbcService)appContext.getBean("abcService");
但是這樣就會存在一個問題:因為它會重新裝載applicationContext-common.xml
并實例化上下文bean延窜,如果有些線程配置類也是在這個配置文件中,那么會造成做相同工作的的線程會被啟兩次袱箱。一次是web容器初始化時啟動,另一次是上述代碼顯示的實例化了一次。當于重新初始化一遍9敬濉!N冒场懈涛!這樣就產(chǎn)生了冗余。
解決方法
不用類似new ClassPathXmlApplicationContext()的方式泳猬,從已有的spring上下文取得已實例化的bean批钠。通過ApplicationContextAware接口進行實現(xiàn)。
當一個類實現(xiàn)了這個接口(ApplicationContextAware)之后得封,這個類就可以方便獲得ApplicationContext中的所有bean埋心。換句話說,就是這個類可以直接獲取spring配置文件中忙上,所有有引用到的bean對象拷呆。
ApplicationContextAware怎么用
(1)方法類AppUtil實現(xiàn)ApplicationContextAware接口
@Component
public class AppUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
applicationContext = arg0;
}
public static Object getObject(String id) {
Object object = null;
object = applicationContext.getBean(id);
return object;
}
}
【備注】
(1)在spring的配置文件中,注冊方法類AppUtil晨横。之所以方法類AppUtil能夠靈活自如地獲取ApplicationContext洋腮,就是因為spring能夠為我們自動地執(zhí)行了setApplicationContext。但是手形,spring不會無緣無故地為某個類執(zhí)行它的方法的啥供,所以,就很有必要通過注冊方法類AppUtil的方式告知spring有這樣子一個類的存在库糠。這里我們使用@Component
來進行注冊伙狐,或者我們也可以像下面這樣在配置文件聲明bean:
<bean id="appUtil" class="com.htsoft.core.util.AppUtil"/>
(2)加載Spring配置文件時,如果Spring配置文件中所定義的Bean類實現(xiàn)了ApplicationContextAware 接口瞬欧,那么在加載Spring配置文件時贷屎,會自動調(diào)用ApplicationContextAware 接口中的
public void setApplicationContext(ApplicationContext context) throws BeansException
方法,獲得ApplicationContext對象,ApplicationContext對象是由spring注入的艘虎。前提必須在Spring配置文件中指定該類唉侄。
(3)使用靜態(tài)的成員ApplicationContext類型的對象。
使用場景備注
從ApplicationContextAware獲取ApplicationContext上下文的情況野建,僅僅適用于當前運行的代碼和已啟動的Spring代碼處于同一個Spring上下文属划,否則獲取到的ApplicationContext是空的恬叹。
比如我要為當前系統(tǒng)加入一個定時任務(wù),定時刷新Memcache緩存同眯。這個定時任務(wù)框架是公司的框架绽昼,下面是我的ApplicationContextAware 接口實現(xiàn)類:
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;//聲明一個靜態(tài)變量保存
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("applicationContext正在初始化,application:"+appContext);
this.applicationContext=applicationContext;
}
public static <T> T getBean(Class<T> clazz){
if(applicationContext==null){
System.out.println("applicationContext是空的");
}else{
System.out.println("applicationContext不是空的");
}
return applicationContext.getBean(clazz);
}
public static ApplicationContext getApplicationContext(){
return applicationContext;
}
}
定時任務(wù)類如下,定時任務(wù)初始化的時候须蜗,首先會調(diào)用作業(yè)類的public static Object getObject()
方法返回作業(yè)類的實例硅确。
@Component
public class RemoteCacheJob extends AbstractSaturnJavaJob {
@Autowired
private AdsRemoteCacheJob adsRemoteCacheJob;
@Autowired
private ILogService logService;
// 實例化的過程:系統(tǒng)會首先調(diào)用作業(yè)類的public static Object getObject()方法,
// 如果返回為null明肮,則調(diào)用作業(yè)類的無參構(gòu)造方法來實例化菱农;否則直接使用getObject()方法返回的對象作為作業(yè)類實例。
public static Object getObject() {
return ApplicationContextUtil.getBean(RemoteCacheJob .class);
}
@Override
public void handleJavaJob(String jobName, Integer shardItem, String shardParam, SaturnJobExecutionContext shardingContext)
throws InterruptedException {
System.out.println("處理定時任務(wù)");
}
}
啟動項目柿估,Spring容器進行初始化大莫,可以看到已經(jīng)初始化了ApplicationContext :
然后運行定時任務(wù)插件,首先去獲取ApplicationContext官份,但是此時的applicationContext是空的:
很顯然只厘,定時任務(wù)是沒辦法獲取到項目所在Spring容器啟動之后的ApplicationContext。