感覺在這個傳回方法中肯定有解決我問題的答案,于是繼續往下查找。
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對象。
是以完全可以這麼做。