Springboot如何獲取上下文ApplicationContext

Springboot獲取上下文ApplicationContext

在項目中遇到瞭一個場景,就是通過獲得上下文然後獲取特定的bean。在此遇到瞭不小的坑,故留下這個篇文章,做個記錄。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
@Component
public class SpringContextUtil implements ApplicationContextAware {
    /**
     * 上下文對象實例
     */
    private static ApplicationContext applicationContext;
 
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
 
    /**
     * 獲取applicationContext
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
 
    /**
     * 通過name獲取 Bean.
     * @param name
     * @return
     */
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }
 
    /**
     * 通過class獲取Bean.
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }
 
    /**
     * 通過name,以及Clazz返回指定的Bean
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }
}

看上面的代碼,可以看到在工具類中一開始是聲明瞭一個ApplicationContext類型的靜態變量,但是由於靜態變量是不能被Spring容器管理的,一開始用正常的getter和setter方法不能獲取到特定的bean,實踐證明,需要在此變量的setter方法上加上@Autowired註解,並且去除setter方法中的static關鍵字。才可實現特定bean的註入。

springboot的應用上下文

springboot上下文有兩個

  • ServletWebServerApplicationContext
  • AnnotationConfigServletWebServerApplicationContext(繼承上面)

ServletWebServerApplicationContext

該類屬於spring-boot-2.1.1Release.jar中,是自springboot誕生就衍生出來的,是spring框架的應用上下文Application的子類。

多說無益,show me code

擴展的功能

首先讓我們來看一下,這個類到底做瞭什麼,有什麼存在的價值?

    private volatile WebServer webServer;
 @Override
 protected void onRefresh() {
  super.onRefresh();
  try {
   createWebServer();
  }
  catch (Throwable ex) {
   throw new ApplicationContextException("Unable to start web server", ex);
  }
 }

在此類中有個WebServer成員變量,讓我們用腳趾頭想一下也應該可以知道,這其實就是web服務對象,也基本上可以猜測就是跟tomcat相關瞭(當然也可以是其他web服務器,如jetty)

然後我們又發現瞭onRefresh方法,相信我們並不陌生,這就是spring核心refresh方法的中一個鉤子方法(即表明此時已經加載所有配置bean),進行WebServer對象的創建

 @Override
 protected void finishRefresh() {
  super.finishRefresh();
  WebServer webServer = startWebServer();
  if (webServer != null) {
   publishEvent(new ServletWebServerInitializedEvent(webServer, this));
  }
 }

我們又發現該類存在finishRefresh,仔細想一下,這個也是spring核心#refresh方法中的一個鉤子方法(不過這個特別,因為該方法是refresh方法中的最後一步,即會去實例化spring容器中的所有beandefinition對象)

首先贊一個,這個很巧妙,調用瞭super.finishRefresh() ,並沒有丟棄父類的邏輯功能(這點在多態中,我相信還是會有人犯錯,本來是擴展功能,但是直接重寫,丟棄瞭父類的方法,當然spring框架開發大佬肯定不會犯這種錯誤,對吧!)

第二點重點來瞭,就是startWebServer,也就是在spring完成實例化之後,就會去啟動web服務。

AnnotationConfigServletWebServerApplicationContext

小結一下:

首先此類是springboot啟動運行run()創建ApplicationContext的實現類,不過很可惜,該類並沒有很強的實質性擴展。

**唯一作用就是擁有瞭通過註解加載配置類的作用,即和AnnotationConfigApplication一樣,隻不過springboot的運行啟動已經是通過註解加載bean類**

(雖然是雞肋,不過這也符合spring創建類的一貫風格,就是每個類都是高內聚的,即每個類除瞭父類的功能之外,還都擁有其他擴展的作用。即使創建出來還沒有用到就被遺棄,但仍然不能阻止spring開發大佬創建該類,哈哈)

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: