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" />

这样子,就会导致Spring加载了两次上下文环境。

解决办法

方法一

只使用一个上下文环境。即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里配置:

	<mvc:annotation-driven />
	<!-- Scans for annotated @Controllers in the classpath -->
	<context:component-scan base-package="xx.yy" use-default-filters="false">
   	    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan

这样子,就表明只扫描@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`