做為一名在.net混了八九年的老兵油子,轉戰java時間並不長,剛開始做項目完全是憑借對C#的認識來做,雖然遇到一些問題,但實際結果顯示C#在語言上和java還是有很大相似度,而且微軟的MVC與Spring MVC也是那麼的神似,這也是為什麼我在做項目前並未對java進行系統的學習也能做項目的原因。最近稍微有些空閒時間,所以決定從基礎開始系統的學習java,這裡並沒有太多高深技術可分享的,本篇給大家分享我解決問題的一個經驗:觸類旁通或者叫舉一反三,對了還有一點,對於存在可優化點的部分要不輕言放棄。
困擾以久的問題:
項目中應用shiro進行登錄權限認證,在Realm實現類中注解服務不成功,得到的實例是null。網上遇到同類問題的還不少,可能每個人的項目情況不同,我並未從中得到正確的解決方案。
@Autowired private ISecurityService securityService;
有兩種方法可以解決:
private ISecurityService securityService; private void initSecurityService() { if (null == this.securityService) { ApplicationContext appCtx = ApplicationContextUtils.getApplicationContext(); this.securityService = appCtx.getBean(ISecurityService.class); } }
private static ISecurityService securityService; @Autowired public void setSecurityService(ISecurityService securityService) { securityService = securityService; }
以上兩種寫法都比較惡心,但當時對於spring類掃描的不了解,也只能做罷。在知識不夠的情況下,如果一味的去鑽也許不一定會有完美的結果,所以我選擇暫時放棄。
項目中遇到過使用數據庫事務不生效的情況,當時同事的解釋是spring掃描的問題,在加載事務配置時,不能掃描Controller。後來有時間研究了下,由於我們使用了spring mvc,而且在web.xml中采用了非常經典的Application Context +DispatcherServlet Context結構。而有意思的是我們並沒有給Application Context配置有關資源掃描以及Bean加載的信息,只有一個shiro配置文件的加載,結果就是項目所有的bean加載都集中在DispatherServlet這個MVC的配置文件中。
也有說如果只存在一個Servlet,那麼可以選擇只使用一個Context,這樣也可以避免誤使用雙親上下文所帶來的問題,這個做法我還沒有嘗試,有時間研究下。
Application Context是什麼?
它是應用程度級別的一個上下文,這個上下文其中一個重要功能就是負責提供對訪問數據庫事務,數據層以及其它一些你想通過應用程序訪問的需求(這裡也許描述的不夠准確,有興趣的可上官方網站上去看文檔)。比如上面的方法一,獲取bean就是通過這個上下文對象動態獲取得到。
DispatcherServlet Context是什麼?
一個應用程序可以定義多個Servlet,每個Servlet都有一個屬於自己的上下文,我們項目中只有一個Servlet。
Application Context與DispatcherServlet Context的關系?
DispatcherServlet Context的父級是Application Context,會繼承所有Application Context所定義的內容。這裡推薦兩個貼子,說的挺清楚的。
為什麼在加載數據庫事務配置前,不能掃描Controller?這個我目前並不知道明細的原因,只大概知道是Spring設計規則問題,DispatherServlet中如果在數據庫事務配置加載前掃描了包含Controller在類的命名空間,結果就是事務並不具備事務能力。所以我們在DispatherServlet配置文件中會出現兩段掃描代碼:
<context:component-scan base-package="cn.wanmei.party" use-default-filters="false"> <context:include-filter expression="org.springframework.web.bind.annotation.Controller" type="annotation"/> </context:component-scan>
<context:component-scan base-package="cn.wanmei.party"> <context:exclude-filter expression="org.springframework.web.bind.annotation.RestController" type="annotation"/> </context:component-scan> <import resource="mybatis.xml"/>
上面這種將幾乎所有配置全部寫在DispatherServlet配置文件中的做法有缺點:
以上大部分都是在描述事務不生效的問題,那與我文前提到的Realm實現類中通過Autowired注解服務失敗有什麼關聯呢?之前提到了Application Context主要功能之一就是提供應用程序對於事務,數據層以及其它類實例的訪問,那麼在Realm實現類中提供類的注解實例當然是在職責范圍內,為此我們需要對配置文件做變更:
<context:component-scan base-package="cn.wanmei.party"> </context:component-scan> <import resource="mybatis.xml"/> <import resource="redis-context.xml"/> <import resource="spring-shiro-web.xml" />
<context:component-scan base-package="cn.wanmei.party"> <context:exclude-filter expression="org.springframework.stereotype.Service" type="annotation"/> </context:component-scan>
解決後:
1:root-context飽滿了
2:Application Conext 有活干了,事務呀什麼的應有的它都有了
3:DispatherServlet的配置清爽了
經過測試,事務正常,Realm中的注解正常,從此再也不需要使用文前提到的那兩種惡心的方式了。本文通過項目中事務不生效的問題,聯想到Realm實現類注解失敗與之存在關聯,最後通過實踐進一步證實推測,也進一步了解了Spring的上下文知識。