0xTTEPX

Just do it, deeply...

Follow me on GitHub

Spring源码阅读引导篇

write by donaldhan, 2017-12-14 16:33

引言

上一篇文章导入spring源码到eclipse,我们从github上拉取spring框架源码,并导入的eclipse,从今天开始,我们将会从spring bean容器,事务,mvc三个方面,去窥探这3个方面的内部机制。 在spring框架源码分析这类文章中,所讲到的spring3,表示spring-3.2.18, 所讲到的spring4,表示spring-4.3.13。所设计到的源码参见spring3-demo。 这篇文章我们作为spring源码阅读部分的引导篇,源码我们使用 spring-4.3.13 版本。

目录

下面我们简单看一下,这3个方面。

spring bean 容器

在我们将spring bean容器的时候,往往回想到应用上下文 ApplicationContext ,因为通过应用上下文,我们获取bean容器中的bean实例。 spring3的时候,我们会用于xml配置的bean声明,并通过 ClassPathXmlApplicationContext 去加载bean上下文配置文件 ApplicationContext.xml,实例如下:

package cn.home.bootstrap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.home.modules.beans.entity.ExampleBean;
import cn.home.util.JacksonUtil;

/**
 * @author donald
 * 2017年11月26日
 * 下午12:45:55
 */
public class ClassPathXmlApplicationContextBoot {
	private static final Logger log = LoggerFactory.getLogger(ClassPathXmlApplicationContextBoot.class);
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext context =
			    new ClassPathXmlApplicationContext(new String[] {"ApplicationContext.xml"});
		ExampleBean exampleBean = context.getBean(ExampleBean.class);
		log.info("exampleBean:{}",JacksonUtil.getInstance().toJson(exampleBean));
	}
}

下面是 ApplicationContext.xml 一个简单配置:

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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/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">
    <!-- scan component and add applicationContext -->
	<context:component-scan base-package="cn.home.modules.transaction" />
	<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<value>classpath:jdbc.properties</value>
		</property>
	</bean>
	<!-- 阿里 druid数据库连接池 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">  
         <!-- 数据库基本信息配置 -->
         <property name="url" value="${jdbc.url}" />  
         <property name="username" value="${jdbc.username}" />  
         <property name="password" value="${jdbc.password}" />  
         <property name="driverClassName" value="${jdbc.driverClassName}" />  
         <property name="filters" value="${filters}" />  
	</bean>  
	<!-- transaction support ,PlatformTransactionMnager -->
	<bean id="txManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	<!-- 开启事务注解时,屏蔽tx:advice,aop:config -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
			<tx:method name="save*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
			<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
			<tx:method name="edit*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
			<tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="UnsupportedOperationException"/>
			<tx:method name="remove*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
		</tx:attributes>
	</tx:advice>
	<aop:config>
		<aop:pointcut id="serviceOperation"
			expression="execution(* cn.home.modules.*.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
	</aop:config>
	<!-- enable transaction annotation support,开始事务注解配置,手动控制那些方法或类需要进行事务控制,
	当开启事务注解时,需要屏蔽上面的tx:advice和aop:config标签的事务声明 -->
	<!-- 	<tx:annotation-driven transaction-manager="txManager" />  -->
</beans>

从spring4开始全面支持注解配置,其实从spring3.2.18开始,spring注解配置已经出现,只不过,在当时的开发环境下,大家还习惯与基于xml的配置, spring4注解配置加载bean的通过注解扫描 @Bean 具体代码如下:

package cn.home.bootstrap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import cn.home.config.AppConfig;
import cn.home.modules.beans.service.FooService;
import cn.home.util.JacksonUtil;

/**
 * @author donald
 * 2017年11月22日
 * 下午9:40:07
 */
public class AnnotationConfigApplicationContextBoot {
	private static final Logger log = LoggerFactory.getLogger(AnnotationConfigApplicationContextBoot.class);
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext  ctx = new AnnotationConfigApplicationContext(AppConfig.class);
	    FooService fooService = ctx.getBean(FooService.class);
	    log.info("fooService:{}",JacksonUtil.getInstance().toJson(fooService));
	    fooService.doStuff(1);
	}
}

注意这个里面使用的上下文配置类是 AnnotationConfigApplicationContext,同时传入的还有一个配置类参数 AppConfig.class , 这个与 ApplicationContext.xml 的作用相同。下面是 AppConfig.class 的一个简单示例:

package cn.home.config;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import com.alibaba.druid.pool.DruidDataSource;

import cn.home.modules.beans.entity.DbInfo;
import cn.home.modules.beans.service.MyService;
import cn.home.modules.beans.service.impl.MyServiceImpl;

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = "cn.home.modules")
@PropertySource("classpath:jdbc.properties")
public class AppConfig implements TransactionManagementConfigurer {
	private static final Logger log = LoggerFactory.getLogger(Logger.class);
	private static final String JDBC_URL = "jdbc.url";
	private static final String JDBC_USERNAME = "jdbc.username";
	private static final String JDBC_PASSWORD = "jdbc.password";
	private static final String JDBC_DRIVE_CLASS_NAME = "jdbc.driverClassName";
	// 初始化时数据库连接数
	private static final int INITIAL_THREAD = 5;
	// 空闲时保存数据库连接数
	private static final int MINIDLE_THREAD = 2;
	// 数据库连接池最大连接数
	private static final int MAX_ACTIVE_THREAD = 100;
	// 数据库连接等待的最长时间
	private static final int MAX_WAIT = 1000;
	// 检查连接是否正常的查询
	private static final String CHECK_CONNECTION_QUERY = "Select 'X'";
	// 连接池状态监控过滤器名
	private static final String STAT_FILETER = "mergeStat";

	// MyBait sql文件
	private static final String MY_BAITS_MAPPER_PATH = "classpath*:mappers/**/**.xml";
	@Autowired
	Environment env;

	/**
	 * durid数据源
	 *
	 * @return
	 * @throws SQLException
	 */
	@Bean(destroyMethod = "close")
	public DataSource dataSource() throws SQLException {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setInitialSize(INITIAL_THREAD);
		dataSource.setMinIdle(MINIDLE_THREAD);
		dataSource.setMaxActive(MAX_ACTIVE_THREAD);
		dataSource.setMaxWait(MAX_WAIT);
		dataSource.setTimeBetweenEvictionRunsMillis(60000);
		dataSource.setValidationQuery(CHECK_CONNECTION_QUERY);
		dataSource.setTestWhileIdle(true);
		dataSource.setTestOnBorrow(false);
		dataSource.setTestOnReturn(false);
		dataSource.setPoolPreparedStatements(false);
		dataSource.setFilters(STAT_FILETER);
		dataSource.setUrl(env.getProperty(JDBC_URL));
		dataSource.setDriverClassName(env.getProperty(JDBC_DRIVE_CLASS_NAME));
		dataSource.setUsername(env.getProperty(JDBC_USERNAME));
		dataSource.setPassword(env.getProperty(JDBC_PASSWORD));
		return dataSource;
	}

	@Bean
	public SqlSessionFactory sqlSessionFactory() throws Exception {
		SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource());
		PathMatchingResourcePatternResolver loader = new PathMatchingResourcePatternResolver();
		sessionFactory.setMapperLocations(loader.getResources(MY_BAITS_MAPPER_PATH));
		return sessionFactory.getObject();
	}

	@Bean
	public SqlSession sqlSession() throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory());
	}

	@Bean
	public PlatformTransactionManager txManager() throws SQLException {
		return new DataSourceTransactionManager(dataSource());
	}

	@Override
	public PlatformTransactionManager annotationDrivenTransactionManager() {
		PlatformTransactionManager transactionManager = null;
		try {
			transactionManager = txManager();
		} catch (SQLException e) {
			log.error("transactionManager config exception...",e);
			e.printStackTrace();
		}
		return transactionManager;
	}

	@Bean
	public MyService myService() {
		return new MyServiceImpl();
	}

	@Bean
	DbInfo dbInfo() {
		DbInfo dbInfo = new DbInfo();
		dbInfo.setUrl(env.getProperty(JDBC_URL));
		dbInfo.setDriverClassName(env.getProperty(JDBC_USERNAME));
		dbInfo.setUsername(env.getProperty(JDBC_PASSWORD));
		dbInfo.setPassword(env.getProperty(JDBC_DRIVE_CLASS_NAME));
		return dbInfo;
	}

}

如果我们想要继承spring的核心容器部分到web,我们可以在 web.xml 中配置,spring3,为:

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

spring4具体配置如下:

<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<context-param>
	<param-name>contextClass</param-name>
	<param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</context-param>

<!-- Configuration locations must consist of one or more comma- or space-delimited
	fully-qualified @Configuration classes. Fully-qualified packages may also
		be specified for component-scanning -->
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>cn.home.config.AppConfig</param-value>
</context-param>
<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

spring事务

在使用spring事务,一般我们会使用声明式事务,在spring3中一般是声明事务如下:

<!-- transaction support ,PlatformTransactionMnager -->
<bean id="txManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
</bean>
<!-- 开启事务注解时,屏蔽tx:advice,aop:config -->
<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
			<tx:method name="save*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
			<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
			<tx:method name="edit*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
			<tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="UnsupportedOperationException"/>
			<tx:method name="remove*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
		</tx:attributes>
</tx:advice>
<aop:config>
	<aop:pointcut id="serviceOperation"
			expression="execution(* cn.home.modules.*.service.*.*(..))" />
	<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
</aop:config>
<!-- enable transaction annotation support,开始事务注解配置,手动控制那些方法或类需要进行事务控制,
当开启事务注解时,需要屏蔽上面的tx:advice和aop:config标签的事务声明 -->
<!-- 	<tx:annotation-driven transaction-manager="txManager" />  -->

spring4的事务配置如下:

package cn.home.config;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import com.alibaba.druid.pool.DruidDataSource;

import cn.home.modules.beans.entity.DbInfo;
import cn.home.modules.beans.service.MyService;
import cn.home.modules.beans.service.impl.MyServiceImpl;

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = "cn.home.modules")
@PropertySource("classpath:jdbc.properties")
public class AppConfig implements TransactionManagementConfigurer {
	private static final Logger log = LoggerFactory.getLogger(Logger.class);
	private static final String JDBC_URL = "jdbc.url";
	private static final String JDBC_USERNAME = "jdbc.username";
	private static final String JDBC_PASSWORD = "jdbc.password";
	private static final String JDBC_DRIVE_CLASS_NAME = "jdbc.driverClassName";
	// 初始化时数据库连接数
	private static final int INITIAL_THREAD = 5;
	// 空闲时保存数据库连接数
	private static final int MINIDLE_THREAD = 2;
	// 数据库连接池最大连接数
	private static final int MAX_ACTIVE_THREAD = 100;
	// 数据库连接等待的最长时间
	private static final int MAX_WAIT = 1000;
	// 检查连接是否正常的查询
	private static final String CHECK_CONNECTION_QUERY = "Select 'X'";
	// 连接池状态监控过滤器名
	private static final String STAT_FILETER = "mergeStat";

	// MyBait sql文件
	private static final String MY_BAITS_MAPPER_PATH = "classpath*:mappers/**/**.xml";
	@Autowired
	Environment env;

	/**
	 * durid数据源
	 *
	 * @return
	 * @throws SQLException
	 */
	@Bean(destroyMethod = "close")
	public DataSource dataSource() throws SQLException {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setInitialSize(INITIAL_THREAD);
		dataSource.setMinIdle(MINIDLE_THREAD);
		dataSource.setMaxActive(MAX_ACTIVE_THREAD);
		dataSource.setMaxWait(MAX_WAIT);
		dataSource.setTimeBetweenEvictionRunsMillis(60000);
		dataSource.setValidationQuery(CHECK_CONNECTION_QUERY);
		dataSource.setTestWhileIdle(true);
		dataSource.setTestOnBorrow(false);
		dataSource.setTestOnReturn(false);
		dataSource.setPoolPreparedStatements(false);
		dataSource.setFilters(STAT_FILETER);
		dataSource.setUrl(env.getProperty(JDBC_URL));
		dataSource.setDriverClassName(env.getProperty(JDBC_DRIVE_CLASS_NAME));
		dataSource.setUsername(env.getProperty(JDBC_USERNAME));
		dataSource.setPassword(env.getProperty(JDBC_PASSWORD));
		return dataSource;
	}

	@Bean
	public SqlSessionFactory sqlSessionFactory() throws Exception {
		SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource());
		PathMatchingResourcePatternResolver loader = new PathMatchingResourcePatternResolver();
		sessionFactory.setMapperLocations(loader.getResources(MY_BAITS_MAPPER_PATH));
		return sessionFactory.getObject();
	}

	@Bean
	public SqlSession sqlSession() throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory());
	}

	@Bean
	public PlatformTransactionManager txManager() throws SQLException {
		return new DataSourceTransactionManager(dataSource());
	}

	@Override
	public PlatformTransactionManager annotationDrivenTransactionManager() {
		PlatformTransactionManager transactionManager = null;
		try {
			transactionManager = txManager();
		} catch (SQLException e) {
			log.error("transactionManager config exception...",e);
			e.printStackTrace();
		}
		return transactionManager;
	}
}

注意开启@EnableTransactionManagement注解配置。

springmvc

开启springmvc 一般我们要会在 web.xml 中进行配置servlet分发器,具体配置如下:

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

spring-mvc配置文件如下:

<?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:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-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/aop
	http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

	<!-- autowired controller -->
	<context:annotation-config />
	<!-- scan component and add applicationContext -->
	<context:component-scan base-package="cn.home.modules.webmvc.controller" />
	<!-- It configures a DefaultServletHttpRequestHandler with a URL mapping
		of "/**" and the lowest priority relative to other URL mappings. This handler
		will forward all requests to the default Servlet. -->
	<mvc:default-servlet-handler />
	<!-- model model binding conversion, JSR-303 support will be detected on
		classpath and enabled automatically -->
	<mvc:annotation-driven>
		<mvc:message-converters register-defaults="true">
			<ref bean="stringHttpMessageConverter" />
			<ref bean="mappingJacksonHttpMessageConverter" />
		</mvc:message-converters>
	</mvc:annotation-driven>
	<!-- 从请求和响应读取/编写字符串。默认情况下,它支持媒体类型 text/* 并使用文本/无格式内容类型编写。 -->
	<bean id="stringHttpMessageConverter"
		class="org.springframework.http.converter.StringHttpMessageConverter">
		<property name="supportedMediaTypes">
			<list>
				<value>text/plain;charset=UTF-8</value>
				<value>text/html;charset=UTF-8</value>
			</list>
		</property>
	</bean>
	<!-- 使用 Jackson 的 ObjectMapper 读取/编写 JSON 数据。它转换媒体类型为 application/json 的数据。 -->
	<bean id="mappingJacksonHttpMessageConverter"
		class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
		<property name="supportedMediaTypes">
			<list>
				<value>application/json;charset=UTF-8</value>
			</list>
		</property>
		<property name="objectMapper">
			<bean class="com.fasterxml.jackson.databind.ObjectMapper">
				<property name="serializationInclusion">
					<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
				</property>
			</bean>
		</property>
	</bean>
	<mvc:interceptors>
		<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
		<mvc:interceptor>
			<mvc:mapping path="/**" />
			<mvc:exclude-mapping path="/admin/**" />
			<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor" />
		</mvc:interceptor>
		<mvc:interceptor>
			<mvc:mapping path="/secure/*" />
			<bean class="cn.home.modules.webmvc.interceptor.SecurityInterceptor" />
		</mvc:interceptor>
	</mvc:interceptors>
	<bean id="exceptionResolver" class="cn.home.modules.webmvc.exception.GlobalHandlerExceptionResolver"></bean>
	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="viewClass"
			value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
	<!-- To enable @AspectJ support with XML based configuration use the aop:aspectj-autoproxy
		element: -->
	<aop:aspectj-autoproxy />
	<!-- <aop:aspectj-autoproxy proxy-target-class="true"/> -->
</beans>

对于spring4,在 web.xml 中进行配置servlet分发器,具体配置如下:

<!-- Declare a Spring MVC DispatcherServlet as usual -->
<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
			instead of the default XmlWebApplicationContext -->
		<init-param>
			<param-name>contextClass</param-name>
			<param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
		</init-param>
		<!-- Again, config locations must consist of one or more comma- or space-delimited
			and fully-qualified @Configuration classes -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>cn.home.config.WebMvcConfig</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
</servlet>
<!-- map all requests for /app/* to the dispatcher servlet -->
<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.do</url-pattern>
</servlet-mapping>

同时开启webmvc注解即可,具体如下:

package cn.home.config;

import java.text.SimpleDateFormat;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

import cn.home.modules.webmvc.Interceptor.CostTimeInterceptor;
import cn.home.modules.webmvc.Interceptor.SystemLogInterceptor;
import cn.home.modules.webmvc.exceptionhandler.GlobalHandlerExceptionResolver;

@Configuration
@ComponentScan(basePackages = "cn.home.modules")
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
	@Autowired
	private CostTimeInterceptor costTimeInterceptor;
	@Autowired
	SystemLogInterceptor systemLogInterceptor;
	@Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                .modulesToInstall(new ParameterNamesModule());
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
    }
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(systemLogInterceptor).addPathPatterns("/**").excludePathPatterns("/admin/**");
		registry.addInterceptor(costTimeInterceptor).addPathPatterns("/**");
	}
	/**
	 * to serve resource requests with a URL pattern of /resources/** from a
	 * public-resources directory within the web application root
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/");
	}
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		InternalResourceViewResolver resolver = new InternalResourceViewResolver();
		resolver.setPrefix("/WEB-INF/jsp/");
		resolver.setSuffix(".jsp");
		resolver.setCache(true);
		resolver.setRequestContextAttribute("request");
		resolver.setContentType("text/html;charset=UTF-8");
		registry.viewResolver(resolver);
	}
	@Bean
	public CommonsMultipartResolver multipartResolver() {
		CommonsMultipartResolver resolver = new CommonsMultipartResolver();
		resolver.setMaxUploadSize(104857600);
		return resolver;
	}
	@Bean
	public GlobalHandlerExceptionResolver globalHandlerExceptionResolver(){
		return new GlobalHandlerExceptionResolver();
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void extendHandlerExceptionResolvers( List list){
		list.add(globalHandlerExceptionResolver());
	}
}

到这里,我们基本把,spring3和spring4的核心配置已经讲完,主要的不同一个是xml,一个是基于注解的。

总结

我们从spring应用上下文、事务、springmvc3个方法分析了spring3和spring4的区别,主要是配置的形式不同。 spring应用上下文是基于spring核心 bean 容器,spring事务主要通过spring aop 和ThreadLocal来实现; spring mvc 主要通过 DispatcherServlet 来分发Web请求。下面我们将从 ClassPathXmlApplicationContext 开始 阅读spring上下文(核心benn容器)相关的代码。