1. 程式人生 > >spring的xml配置與annotation註解混合

spring的xml配置與annotation註解混合

問題:

在LVMMCrawlerSuit.java是xml配置的bean, 需要注入用@Component宣告的bean. 但是執行的時候卻報NullPointerException. 說明沒有注入進來.

程式碼:

1. java

Java程式碼  收藏程式碼
  1. 1. LVMMCrawlerSuit.java  
  2.  public class LVMMCrawlerSuit extends AbstractCrawlerSuit{  
  3.     @Resource  
  4.     private LVMMURLBuilder lvmmurlBuilder;  
  5.     public LVMMCrawlerSuit() {  
  6.     }  
  7. }  
  8. 2. LVMMURLBuilder.java  
  9. @Component  
  10. public class LVMMURLBuilder extends AbstractResourceURLBuilder {  
  11.     public LVMMURLBuilder() {    }  
  12. }  

2. 配置.

Xml程式碼  收藏程式碼
  1. <bean id="LVMMCrawlerSuit" class = 'com.qunar.b2c.crawlersuit.impl.LVMMCrawlerSuit'/>   

問題查詢:

1. 查詢網上資源,未果.

2. 果斷debug,跟蹤原始碼.

       將斷點定位到 org.springframework.context.support.AbstractRefreshableApplicationContext#loadBeanDefinitions ,該方法是載入bean的必經之路.跟蹤發現,該方法共執行兩次,生成了兩個不同的 org.springframework.beans.factory.support.DefaultListableBeanFactory, 並且後者的parentBeanFactory為前者,根據原設計是後者可以呼叫前者的bean 並完成注入.

      現在報NullPointerException,很明顯是"父呼叫子",所以肯定拿不到.在列印的log中進行了佐證.

      在debug中發現,兩次執行分別來自不同的beans資原始檔: app-web.xml 和 applicationContext-*.xml, 按key查詢,很容易找到了配置資訊如下.

Xml程式碼  收藏程式碼
  1. <servlet>  
  2.         <servlet-name>dispatcherServlet</servlet-name>  
  3.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  4.         <init-param>  
  5.             <param-name>contextConfigLocation</param-name>  
  6.             <param-value>  
  7.                 classpath:classpath:app-web.xml  
  8.             </param-value>  
  9.         </init-param>  
  10.         <load-on-startup>1</load-on-startup>  
  11.     </servlet>  
  12.         <context-param>  
  13.         <param-name>contextConfigLocation</param-name>  
  14.         <param-value>classpath:applicationContext-*.xml</param-value>  
  15.     </context-param>  

    既然,兩次載入,並且載入了不同的beans,雖然有父子的層級關係,但是限制多多. 那麼就嘗試合二為一.

    在test中,發現因為修改了spring預設載入的檔名,所以刪除任何一個配置都不能正確執行.那麼就全部設定成一樣的吧. test success......

解決方案:

    方案一. 將配置檔案路徑合併, 分別指定給不同配置.

Xml程式碼  收藏程式碼
  1. <servlet>  
  2.         <servlet-name>dispatcherServlet</servlet-name>  
  3.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  4.         <init-param>  
  5.             <param-name>contextConfigLocation</param-name>  
  6.             <param-value>  
  7.                 classpath:applicationContext-*.xml,classpath:app-web.xml  
  8.             </param-value>  
  9.         </init-param>  
  10.         <load-on-startup>1</load-on-startup>  
  11.     </servlet>  
  12.         <context-param>  
  13.         <param-name>contextConfigLocation</param-name>  
  14.         <param-value>classpath:applicationContext-*.xml,classpath:app-web.xml</param-value>  
  15.     </context-param>  

    方案二. 原有配置不變,合理規劃Bean的定義及合理使用.

         在方案一中, 使用的簡單,粗暴的解決辦法. 沒有考慮到spring的設計思想. 既然有ioc容器的父子級劃分,那麼在使用的時候,一定會有用的.

         在使用annotation定義bean 的時候,是需要增加如下程式碼,對使用何種註解的類才管理到ioc容器中.

Xml程式碼  收藏程式碼
  1. <context:component-scan base-package="com.qunar.b2c">  
  2.        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />  
  3.        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />  
  4.        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />  
  5.        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />  
  6.    </context:component-scan>  

        上述提到, 在 spring web的使用中, 會載入兩個ioc容器,

          1. 一個是contextConfigLocation定義,用來啟動spring核心框架的. 所以在該步驟中,應載入應用中的基礎服務資訊的bean,如 dao,Service 等等.

          2. 另外一個ioc容器是web載入的容器, 那麼只需載入Controller相關的bean.

        因為在spring ioc的 DefaultListableBeanFactory類是支援父子關係,

            1. 子容器是可以訪問到父容器中的bean,

            2. 然而父容器訪問不了子容器的bean,

        這就保證了, Controller可以訪問 Service等, 但是Service 訪問不了web層的bean, 這樣就將職責分開了.所以修改的配置如下:

Xml程式碼  收藏程式碼
  1. applicationContext-beans.xml:  
  2. <context:component-scan base-package="com.qunar.b2c">  
  3.         <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />  
  4.         <context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />  
  5.         <context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />  
  6.     </context:component-scan>  
  7. app-web.xml  
  8.  <context:component-scan base-package="com.qunar.b2c">  
  9.         <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />  
  10.    </context:component-scan>  

     在開發定義bean的時候, 也需要注意,把bean定義到哪一層級.