天天看點

Spring 配置檔案的ContextLoaderListener原了解析(中)

感覺在這個傳回方法中肯定有解決我問題的答案,于是繼續往下查找。

Object attr = sc.getAttribute(attrName);
  return (WebApplicationContext) attr      

這不就是initWebApplicationContext方法中setAttribute進去的WebApplicationContext嗎?是以可以确信得到servletContext也可以得到webApplicationContext。

那麼問題又來了,通過servletContext可以得到webApplicationContext有什麼意義嗎?

上面我們提到“把建立好的springcontext,交給application内置對象,提供給監聽器/過濾器/攔截器使用”。

假設我們有一個需求是要做首頁顯示。平時的代碼經常是在控制器控制傳回結果給前台的,那麼第一頁需要怎麼去顯示呢。抽象得到的問題是如何在一開始拿到資料。

能想到的大緻的解決方案有三種:

+++++++++++++++++++++++++++++++++++++++++++++++

1.可以通過ajx異步加載的方式請求背景資料,然後呈現出來。

2.頁面重定向的思路,先把查詢請求交給控制器處理,得到查詢結果後轉到首頁綁定資料并顯示。

3.在Ioc容器初始化的過程中,把資料查詢出來,然後放在application裡。

三種方案都能實作首頁顯示,不過前兩種方法很大的弊端就是需要頻繁操作資料庫,會對資料庫造成一定的壓力。而同樣地實作監聽器邏輯的第三種方法也有弊端。就是無法實時更新,不過資料庫壓力相對前兩種不是很大。針對無法實時更新這一問題有成熟的解決方案,可以使用定時器的思路。隔一段時間重新開機一次。目前來說有許多網站都是這麼做的。

而對于首頁這種通路量比較大的頁面,如果說最好的解決方案是實作靜态化技術。

前陣子考慮寫一篇關于僞靜态化的文章。當然和靜态化還是有差別的。好了,回到我們listener的實作上來。

我們說過“ContextLoaderListener實作了ServletContextListener接口。伺服器啟動時contextInitialized會被調用”。加載容器時能取出資料,那麼我們需要實作這個接口。

@Service
public class CommonListener implements ServletContextListener{
 
  @Autowired
  private UserService userService;
 
  public void contextInitialized(ServletContextEvent servletContextEvent) {
      //Exception sending context initialized event to listener instance of class com.walidake.listener.CommonListener java.lang.NullPointerException
      System.out.println(userService.findUser());
  }
 
  public void contextDestroyed(ServletContextEvent servletContextEvent) {
      // TODO Auto-generated method stub
    
  } 
 
       

需要注意一件事!

spring是管理邏輯層和資料通路層的依賴。而listener是web元件,那麼必然不能放在spring裡面。真正執行個體化它的應該是tomcat,在啟動加載web.xml執行個體化的。上層的元件不可能被下層執行個體化得到。

是以,即使交給Spring執行個體化,它也沒能力去幫你執行個體化。真正實作執行個體化的還是web容器。

然而NullPointerException并不是來自這個原因,我們說過“ContextLoader來完成實際的WebApplicationContext,也就是Ioc容器的初始化工作”。我們并沒有繼承ContextLoader,沒有Ioc容器的初始化,是無法實作依賴注入的。

是以,我們想到另一種解決方案,能不能通過new ClassPathXmlApplicationContext的方式,像測試用例那樣取得Ioc容器中的bean對象。

發現可以正常列印出結果。然而觀察日志後發現,原本的單例被建立了多次(譬如userServiceImpl等)。是以該方法并不可取。

那麼,由于被建立了多次,是不是可以說明項目中已存在了WebApplicationContext?

是的。我們一開始說“在初始化ContextLoaderListener成功後,spring context會存放在servletContext中”,意味着我們完全可以從servletContext取出WebApplicationContext,然後getBean取得需要的bean對象。

是以完全可以這麼做。