ApplicationContextAware使用理解

問題背景

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

1、BeanFactory是什麼?

BeanFactory作爲基礎的IoC容器,管理了spring所有的Bean,提供了最基本的容器功能,但是BeanFactory是一個接口。

2、ApplicationContext是什麼?

ApplicationContext 是一個高級形態的 IoC 容器,它在 BeanFactory 基礎上附加了好多其他功能, 提供了更多面向框架的功能,因此我們一般使用 ApplicationContext 進行開發
 

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對象。

二、怎麼使用這個接口?

1、自定義的類,實現ApplicationContextAware接口,例如ApplicationContextUtil實現接口,代碼如下:

package com.upincar.contract.util;
 
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
 
public class ApplicationContextUtil implements ApplicationContextAware{
 
    private static ApplicationContext applicationContext;
 
    /**
     * 實現ApplicationContextAware接口的回調方法,設置上下文環境
     *
     * @param applicationContext spring上下文對象
     * @throws BeansException 拋出spring異常
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextUtil.applicationContext = applicationContext;
    }
 
    /**
     * @return ApplicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
 
    /**
     * 獲取對象
     *
     * @param name spring配置文件中配置的bean名或註解的名稱
     * @return 一個以所給名字註冊的bean的實例
     * @throws BeansException 拋出spring異常
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        return (T) applicationContext.getBean(name);
    }
 
    /**
     * 獲取類型爲requiredType的對象
     *
     * @param clazz 需要獲取的bean的類型
     * @return 該類型的一個在ioc容器中的bean
     * @throws BeansException 拋出spring異常
     */
    public static <T> T getBean(Class<T> clazz) throws BeansException {
        return applicationContext.getBean(clazz);
    }
 
    /**
     * 如果ioc容器中包含一個與所給名稱匹配的bean定義,則返回true否則返回false
     *
     * @param name ioc容器中註冊的bean名稱
     * @return 存在返回true否則返回false
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }
}
 
2、在spring的配置文件中,註冊方法類ApplicationContextUtil

注:方法類ApplicationContextUtil是一個bean,之所以方法類ApplicationContextUtil能夠靈活自如地獲取ApplicationContext就是因爲spring能夠爲我們自動地執行了setApplicationContext。

但是spring不會無緣無故地爲某個類執行它的方法的,所以就很有必要通過註冊方法類ApplicationContextUtil的方式告知spring有這樣子一個類的存在;

將方法類ApplicationContextUtil作爲一個普通的bean在spring的配置文件中進行註冊,在xml文件中配置如下:

<!-- 注入ApplicationContextAware接口實現類,spring上下文環境 -->
    <bean id="applicationContextUtil" class="com.upincar.contract.util.ApplicationContextUtil"/>
3、ApplicationContextUtil類的使用

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

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

定時任務類如下,定時任務初始化的時候,首先會調用作業類的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 :

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

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

ApplicationContextAware接口在springboot中的使用

由於springboot默認掃描規則是:自動掃描啓動器類的同包或者其子包的下的註解。

所以在springboot項目中不需要在xml中配置,只需要在ApplicationContextUtil類中使用@Component註解,把ApplicationContextUtil註冊爲spring的組件,就可以在項目中使用ApplicationContextUtil獲取spring容器中的某個Bean了。
 

 

 

發佈了43 篇原創文章 · 獲贊 11 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章