spring mvc 配置失效了?

版本:spring 3.0以上html

項目中有兩個spring的配置xml,以下java

${project.dir}\src\main\resources\META-INF\spring\applicationContext-service-database.xml
${project.dir}\src\main\webapp\WEB-INF\database-servlet.xml

項目中的web.xmlweb

${project.dir}\src\main\webapp\WEB-INF\web.xml

web.xml的配置以下:spring

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath*:META-INF/spring/applicationContext-service-database.xml
    </param-value>
</context-param> 
<listener>
    <listener-class> org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
 
 
<servlet>
    <servlet-name>database</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>database</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

一、ContextLoaderListener加載applicationContext-service-database.xml

二、DispatcherServlet加載database-servlet.xml。若是不配置contextConfigLocation的話,DispatcherServlet會默認查找${project.dir}\src\main\webapp\WEB-INF\{servlet-name}-servlet.xml。

      如本例:servlet-name=database,則文件名爲database-servlet.xml,全部會查找${project.dir}\src\main\webapp\WEB-INF\database-servlet.xml

具體查看文檔https://docs.spring.io/spring/docs/3.2.18.RELEASE/spring-framework-reference/htmlsingle/#mvc-introduction中17.2api

三、這樣配置最終spring會產生兩個容器,ContextLoaderListener會生成容器A,DispatcherServlet會生成容器B,容器B的父容器是容器A,容器A的父容器爲null

四、容器B會繼承容器A的部分功能(其實這樣作主要是爲了防止單例bean的重複注入和容器B能夠獲取到容器A的bean,這樣進行bean共享,可是容器A獲取不到容器B的bean)

五、這樣的配置spring mvc就有了,不須要在database-servlet.xml中配置<mvc:annotation-driven/>

問題:

若是在applicationContext-service-database.xml配置<mvc:annotation-driven/>,而在database-servlet.xml沒有配置<mvc:annotation-driven/>,這樣就會致使spring mvc功能失效,訪問地址會404

緣由:

由於若是在applicationContext-service-database.xml配置<mvc:annotation-driven/>,就會致使ContextLoaderListener加入容器A提早把spring mvc加載到容器A中,可是此時容器A,並無database-servlet.xml中對應須要向外暴露的接口、url或controller。當DispatcherServlet加載容器B的時候發現,它的父容器A中已經存在spring mvc的相關功能,就不會重複加載,因此就會致使spring mvc的功能失效

關鍵代碼:mvc

org.springframework.web.servlet.DispatcherServlet
private void initHandlerMappings(ApplicationContext context) {
   this.handlerMappings = null;
 
   if (this.detectAllHandlerMappings) {
      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
      // BeanFactoryUtils.beansOfTypeIncludingAncestors 會去獲取容器B和它的父容器A中的HandlerMapping的接口實現,若是ContextLoaderListener已經配置了<mvc:annotation-driven/>
      // ,則直接使用ContextLoaderListener加載的HandlerMapping
      Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
         this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
         // We keep HandlerMappings in sorted order.
         OrderComparator.sort(this.handlerMappings);
      }
   }
   else {
      try {
         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
         this.handlerMappings = Collections.singletonList(hm);
      }
      catch (NoSuchBeanDefinitionException ex) {
         // Ignore, we'll add a default HandlerMapping later.
      }
   }
 
   // Ensure we have at least one HandlerMapping, by registering
   // a default HandlerMapping if no other mappings are found.
   // 若是在任何配置文件中都沒有配置spring mvc 功能,如:<mvc:annotation-driven/>,則會使用默認的HandlerMapping,默認的HandlerMapping是
   //org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping, org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
   // 相關默認配置請查看org\springframework\web\servlet\DispatcherServlet.properties
   if (this.handlerMappings == null) {
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
      if (logger.isDebugEnabled()) {
         logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
      }
   }
}

這只是spring mvc 其中一個功能,其餘相關功能請查看DispatcherServletinit*方法app

解決辦法:
不要在ContextLoaderListener加載任何與spring mvc相關的功能

最後
ContextLoaderListener就是爲了加載公用的類而存在的
通常ContextLoaderListener只有一個,而DispatcherServlet能夠有多個,每一個DispatcherServlet都會默認的以ContextLoaderListener加載的容器爲父容器。

因此請按照spring 的套路來,減小沒必要須的問題

相關文章
相關標籤/搜索