以下文章爲轉載內容,但有些疑問再下方提出,請各位老師賜教。
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.xml
和spring-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
這些註解的Beancontext: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`
以上文章爲轉載內容,這裏有個疑問,web容器啓動時若按照方法二進行配置,在controller中引入了service層的一個 bean(@Service("iAdminUserService")),controller中配置如下:
@Resource
private IAdminUserService iAdminUserService;
此時就會報以下異常:
.NoSuchBeanDefinitionException: No qualifying bean of type [com..service.IAdminUserService]
所以只能先用第一種方法,刪除spring容器中的自動掃描,只加springmvc中的自動掃描。
此處留下個疑問,待以後有時間再進行測試,若有大師看到此處有好的解決辦法,就請留言給我,小弟不勝感激。