Spring MVC 里加載兩次Bean的解決辦法(請看最下面的疑問)

以下文章爲轉載內容,但有些疑問再下方提出,請各位老師賜教。

SpringMVC 裏上下文的概念

web.xml 裏的配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        version="2.5">
   <display-name>uniweibov2</display-name>


   <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>

   <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath:spring.xml</param-value>
   </context-param>

   <servlet>
       <servlet-name>mvc-dispatcher</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:spring-servlet.xml</param-value>
       </init-param>
       <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
       <servlet-name>mvc-dispatcher</servlet-name>
       <url-pattern>/</url-pattern>
   </servlet-mapping>

</web-app>


在SpringMVC裏,有兩種上下文。一種是:Application Context,還有一種是Web Application Context.

application context (parent)

<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:spring.xml</param-value>
</context-param>


這樣的配置,就是在配置application context,也就是parent application context


web application context (child)

<servlet>
       <servlet-name>mvc-dispatcher</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:spring-servlet.xml</param-value>
       </init-param>
       <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
       <servlet-name>mvc-dispatcher</servlet-name>
       <url-pattern>/</url-pattern>
   </servlet-mapping>

parent 是共享於 child的,也就是說,在parent中定義的東西,都可以在child中使用.

Bean加載兩次的原因:

這是由於在這兩個context裏,配置的時候導致Spring掃描時,掃描並加載了兩次Bean.

例如,在這兩個spring.xmlspring-servlet.xml配置文件裏,都配置了:

<context:component-scan base-package="xx.yy" />


解決辦法

方法一

只使用一個上下文環境。即Bean的定義,只放在一個配置文件裏,讓另一個配置文件爲空,即如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">


</beans>

這兩個,只要一個爲空,另一個不爲空即可.


方法二

在這兩個配置文件裏,只是沒有配置重複的內容即可.即如果在parent裏定義了的Bean,就不要在child裏定義了。這時,可以使用如下的方式來配置:

spring.xml裏配置:

<context:annotation-config />
  <context:component-scan base-package="xx.yy">
      <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  </context:component-scan>

這樣子,就表明掃描除了有@Controller之外的所有註解的Bean.

spring-servlet.xml裏配置:

這樣子,就表明只掃描@Controller註解的Bean.注意這裏的user-default-filters="false"

包掃描詳解

<context:component-scan base-package="xx.yy" use-default-filters="false">

base-package:要掃描的包
use-default-filters:是否使用默認的過濾器,默認爲true,即掃描@Component, @Repository, @Service, @Controller這些註解的Bean
context:include-filter:使用白名單過濾器
context:exclude-filter:使用黑名單過濾器

Spring 的使用順序是: 先 exclude-filter,再到include-filter

在源碼org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider的方法裏:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if (!metadata.isAnnotated(Profile.class.getName())) {
return true;
}
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
return this.environment.acceptsProfiles(profile.getStringArray("value"));
}
}
return false;
}

type的類型還有:



type=`annotation`
type=`assignable`
type=`aspectj`
type=`regex`
type=`custom`


轉自:http://emacsist.github.io/2015/11/23/Spring-MVC-%E9%87%8C%E5%8A%A0%E8%BD%BD%E4%B8%A4%E6%AC%A1Bean%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/


以上文章爲轉載內容,這裏有個疑問,web容器啓動時若按照方法二進行配置,在controller中引入了service層的一個 bean(@Service("iAdminUserService")),controller中配置如下:

@Resource
private IAdminUserService iAdminUserService;

此時就會報以下異常:

.NoSuchBeanDefinitionException: No qualifying bean of type [com..service.IAdminUserService]

所以只能先用第一種方法,刪除spring容器中的自動掃描,只加springmvc中的自動掃描。


此處留下個疑問,待以後有時間再進行測試,若有大師看到此處有好的解決辦法,就請留言給我,小弟不勝感激。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章