Spring ApplicationContextAware使用理解

問題背景

在我們的web程序中,用spring來管理各個實例(bean), 有時在程序中為了使用已被實例化的bean, 通常會用到這樣的代碼:

ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext-common.xml");
AbcService abcService = (AbcService)appContext.getBean("abcService");

但是這樣就會存在一個問題:因為它會重新裝載applicationContext-common.xml並實例化上下文bean,如果有些線程配置類也是在這個配置文件中,那麼會造成做相同工作的的線程會被啟兩次。一次是web容器初始化時啟動,另一次是上述代碼顯示的實例化了一次。當於重新初始化一遍!!!!這樣就產生了冗餘。

解決方法

不用類似new ClassPathXmlApplicationContext()的方式,從已有的spring上下文取得已實例化的bean。通過ApplicationContextAware接口進行實現。

當一個類實現了這個接口(ApplicationContextAware)之後,這個類就可以方便獲得ApplicationContext中的所有bean。換句話說,就是這個類可以直接獲取spring配置文件中,所有有引用到的bean對象。

ApplicationContextAware怎麼用

(1)方法類AppUtil實現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能夠為我們自動地執行了setApplicationContext。但是,spring不會無緣無故地為某個類執行它的方法的,所以,就很有必要通過註冊方法類AppUtil的方式告知spring有這樣子一個類的存在。這裡我們使用@Component來進行註冊,或者我們也可以像下面這樣在配置文件聲明bean:

<bean>

(2)加載Spring配置文件時,如果Spring配置文件中所定義的Bean類實現了ApplicationContextAware 接口,那麼在加載Spring配置文件時,會自動調用ApplicationContextAware 接口中的

public void setApplicationContext(ApplicationContext context) throws BeansException

方法,獲得ApplicationContext對象,ApplicationContext對象是由spring注入的。前提必須在Spring配置文件中指定該類。

(3)使用靜態的成員ApplicationContext類型的對象。

使用場景備註

從ApplicationContextAware獲取ApplicationContext上下文的情況,僅僅適用於當前運行的代碼和已啟動的Spring代碼處於同一個Spring上下文,否則獲取到的ApplicationContext是空的。

比如我要為當前系統加入一個定時任務,定時刷新Memcache緩存。這個定時任務框架是公司的框架,下面是我的ApplicationContextAware 接口實現類:

@Component
public class ApplicationContextUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;//聲明一個靜態變量保存

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("applicationContext正在初始化,application:"+appContext);
this.applicationContext=applicationContext;
}

public static T getBean(Class clazz){
if(applicationContext==null){
System.out.println("applicationContext是空的");
}else{
System.out.println("applicationContext不是空的");
}
return applicationContext.getBean(clazz);
}

public static ApplicationContext getApplicationContext(){
return applicationContext;
}
}

定時任務類如下,定時任務初始化的時候,首先會調用作業類的public static Object getObject()方法返回作業類的實例。

@Component
public class RemoteCacheJob extends AbstractSaturnJavaJob {

@Autowired
private AdsRemoteCacheJob adsRemoteCacheJob;

@Autowired
private ILogService logService;

// 實例化的過程:系統會首先調用作業類的public static Object getObject()方法,
// 如果返回為null,則調用作業類的無參構造方法來實例化;否則直接使用getObject()方法返回的對象作為作業類實例。
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("處理定時任務");
}
}

啟動項目,Spring容器進行初始化,可以看到已經初始化了ApplicationContext :

Spring   ApplicationContextAware使用理解

然後運行定時任務插件,首先去獲取ApplicationContext,但是此時的applicationContext是空的:

Spring   ApplicationContextAware使用理解

很顯然,定時任務是沒辦法獲取到項目所在Spring容器啟動之後的ApplicationContext。


分享到:


相關文章: