SpringMVC(一) 初始化

Posted by ZhouJ000 on September 28, 2018
最后更新于:2018-10-01

SpringMVC(一) 初始化
SpringMVC(二) 请求调度
SpringMVC(三) 消息转换器
SpringMVC(四) 文件上传与下载

MVC

MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范,用一种将业务逻辑、数据、显示分离的方法组织代码,MVC主要作用是降低了视图与业务逻辑间的双向偶合。MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异

MVC,MVP 和 MVVM 的图示
MVC、MVP、MVVM 模式

Servlet

Spring MVC是基于Servlet功能实现的

servlet的生命周期:
1、初始化阶段: 无参构造函数、init()方法
2、响应客户请求阶段: service()方法 -> doGet/doPost…
3、终止阶段: destory()方法
(4、JVM垃圾回收器回收)

Servlet初始化阶段:
1、servlet容器加载servlet类,把servlet类的.class文件中的数据读取到内存中
2、servlet容器创建一个ServletConfig对象。ServletConfig对象包含了servlet的初始化配置信息
3、servlet容器创建一个servlet对象
4、servlet容器调用servlet对象的init方法进行初始化

Servlet运行阶段:
当servlet容器接受到一个请求时,servlet容器会针对这个请求创建servletRequest与servletResponse对象,然后调用service方法。并把这2个参数传递给该方法。处理后再通过servletResponse对象生成响应结果。然后销毁servletRequest与servletResponse

Servlet销毁阶段:
当web容器被终止时,servlet容器会先调用servlet对象的destrory方法,然后再销毁servlet对象,同时也会销毁与servlet对象相关联的servletConfig对象

servlet框架由两个java包组成: javax.servlet和javax.servlet.http。在javax.servlet包中定义了所有的servlet类都必须实现或扩展的通用接口和类,在javax.servlet.http包中定义了采用HTTP通信协议的HttpServlet类

创建Servlet对象的时机:
1、Servlet容器启动时: 读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法,这是由web.xml文件中为Servlet设置的<load-on-startup>属性决定的
2、在Servlet容器启动后: 客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet对象的service()方法

注: servlet对象在tomcat服务器是单实例多线程的

Spring MVC配置

配置文件

1、web.xml中配置servlet与servlet-mapping拦截,自动加载DispatcherServlet
2、创建配置文件,比如SpringMvcContext.xml
3、配置context:component-scan自动扫包,配置mvc:annotation-driven启用注解映射的支持,配置mvc:default-servlet-handler不处理静态资源,配置InternalResourceViewResolver视图解析器

// web.xml
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	<!-- <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> -->
</listener>
<!-- 配置Spring(父上下文) -->
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:spring/applicationContext*.xml</param-value>	
</context-param>
<!-- 配置SpringMVC(子上下文) -->
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/SpringMvcContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
	<servlet-name>dispatcherServlet</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>


// springMVC配置文件与spring配置文件可以用一份,多份下要防止重复bean
// SpringMvcContext.xml
<!-- 自动扫描包 -->
<context:component-scan base-package="com.test.mvc" />
<!-- @Autowired -->
<context:annotation-config />
<!-- 注解映射的支持: 自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter -->
<mvc:annotation-driven />
<!-- 视图解析器:多个解析器可通过order排序 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="viewClass">
		<value>org.springframework.web.servlet.view.JstlView</value>
	</property>
	<property name="prefix" value="/WEB-INF/views/"></property>
	<property name="suffix" value=".jsp"></property>
	<property name="contentType" value="text/html; charset=UTF-8" />
	<property name="order" value="1" />
</bean>

<!-- 对静态资源文件的访问: 使用默认的Servlet来响应静态文件,交回Web应用服务器处理 -->
<mvc:default-servlet-handler />
<!-- 由Spring MVC框架自己处理静态资源,并添加一些有用的附加功能 -->
<mvc:resources location="/js/" mapping="/js/**" />
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/images/" mapping="/images/**" />
<!-- 拦截器 -->
<mvc:interceptors>
	<mvc:interceptor>
		<mvc:mapping path="/**" />
		<mvc:exclude-mapping path="/js/**"/>
		<mvc:exclude-mapping path="/css/**"/>
		<mvc:exclude-mapping path="/images/**"/>
		<mvc:exclude-mapping path="/docs/**"/>
		<bean class="com.test.interceptor.DemoInterceptor"></bean>
	</mvc:interceptor>
</mvc:interceptors>

parentandson 注: 不使用listener监听器加载spring配置文件,只使用DispatcherServlet加载spring配置,不要父子上下文(子可访问父,父不可访问子),有时可以避免一些父子访问问题,重复扫包bean多次加载问题,父子上下文导致的事务问题等等

java配置

实现WebApplicationInitializer接口来配置Servlet3.0+配置,替代web.xml,实现该接口自动被SpringServletContainerInitializer获取到

public class MyWebInitializer implements WebApplicationInitializer {
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		// 注册配置类,和当前servletContext关联
		AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
		ctx.register(MyMvcConfig.class);
		ctx.setServletContext(servletContext);
		
		// 注册Spring MVC的DispatcherServlet
		Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
		servlet.addMapping("/");
		servlet.setLoadOnStartup(1);
		
		servlet.setAsyncSupported(true); // 开启servlet 3.0+异步方法处理
	}
}


@EnableScheduling
// 最小配置: 
@Configuration
@EnableWebMvc  // is equivalent to <mvc:annotation-driven /> in XML
@ComponentScan("com.test.mvc")
public class MyMvcConfig extends WebMvcConfigurerAdapter {
	@Bean
	public InternalResourceViewResolver viewResolver() {
		// ViewResolver:Spring MVC视图渲染的核心机制
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");
		viewResolver.setViewClass(JstlView.class);
		
		return viewResolver;
	}
	
	// 配置静态资源的处理:要求DispatcherServlet将对静态资源的请求转发到Servlet容器中默认的Servlet上,而不使用DispatcherServlet本身来处理此类请求
//	@Override
//	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//		configurer.enable();
//	}

	// 静态资源映射
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		// ResourceLocations指文件放置目录,ResourceHandler指对外暴露的访问路径
		registry.addResourceHandler("/images/**").addResourceLocations("classpath:/images/");
	}
	
	// 配置拦截器Bean
	@Bean
	public DemoInterceptor demoInterceptor() {
		return new DemoInterceptor();
	}

	// 注册拦截器
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(demoInterceptor());
	}
	
	// 快捷的ViewController
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		// 简洁的页面跳转
		registry.addViewController("/helloSpring").setViewName("/index");
		registry.addViewController("/toupload").setViewName("/upload");
	}
	
	// 上传文件
	@Bean
	public MultipartResolver multipartResolver() {
		CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
		// multipartResolver.setUploadTempDir("/tmp/uploads");
		multipartResolver.setMaxUploadSize(1000000);
		return multipartResolver;
	}

	// 添加一个自定义的HttpMessageConverter,不覆盖默认注册的HttpMessageConverter
	@Override
	public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		converters.add(converter());
	}
	@Bean
	public MyMessageConverter converter() {
		return new MyMessageConverter();
	}
}

另一种方式:替代web.xml:只能部署到支持Servlet3.0的服务器中才能正常工作,比如tomcat7或更高。Servlet3.0容器会在类路径中查找ServletContainerInitializer接口的类,发现会用它来配置Servlet容器。Spring提供了该接口实现,并反过来查找实现了WebApplicationInitializer的类并将配置任务交给它们来完成。Spring3.2引入一个基础实现:AbstractAnnotationConfigDispatcherServletInitializer,同时创建DispatcherServlet和ContextLoaderListener

@Configuration
@ComponentScan(basePackages = {"com.test.mvc"},
		excludeFilters = {
				@Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class)
		})
public class RootConfig {
	// 可以定义一些bean...
}


public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {	
	// 定义ContextLoaderListener应用上下文中的bean
	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class<?>[]{RootConfig.class}; 
	}
	
	// 定义DispatcherServlet应用上下文中的bean
	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class<?>[]{MyMvcConfig.class}; 
	}
	
	// 将DispatcherServlet映射到“/”
	@Override
	protected String[] getServletMappings() {
		return new String[]{"/"};
	}
	...
}	

源码分析

ContextLoaderListener

先来看一下ContextLoaderListener做了什么,在启动web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener接口,在web.xml里配置这个监听器,启动容器时,会默认执行它实现的方法。每个Web应用都有一个ServletContext与之关联。ServletContext对象在应用启动时被创建,在应用关闭的时候被销毁。ServletContext在全局范围内有效。在ServletContextListener中的核心逻辑就是初始化WebApplicationContext实例并存放至ServletContext中

ServletContext启动之后会调用ServletContextListener的contextInitialized方法

// ContextLoaderListener
public void contextInitialized(ServletContextEvent event) {
	// 初始化WebApplicationContext
	initWebApplicationContext(event.getServletContext());
}


// ContextLoader
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	// web.xml中存在多次ContextLoader定义,在配置中只允许声明一次ServletContextListener
	if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
		throw new IllegalStateException(
				"Cannot initialize context because there is already a root application context present - " +
				"check whether you have multiple ContextLoader* definitions in your web.xml!");
	}

	Log logger = LogFactory.getLog(ContextLoader.class);
	servletContext.log("Initializing Spring root WebApplicationContext");
	if (logger.isInfoEnabled()) {
		logger.info("Root WebApplicationContext: initialization started");
	}
	long startTime = System.currentTimeMillis();

	try {
		// Store context in local instance variable, to guarantee that
		// it is available on ServletContext shutdown.
		if (this.context == null) {
			// ----> 创建WebApplicationContext
			this.context = createWebApplicationContext(servletContext);
		}
		if (this.context instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {
				// The context has not yet been refreshed -> provide services such as
				// setting the parent context, setting the application context id, etc
				if (cwac.getParent() == null) {
					// The context instance was injected without an explicit parent ->
					// determine parent for root web application context, if any.
					// 设置父上下文
					ApplicationContext parent = loadParentContext(servletContext);
					cwac.setParent(parent);
				}
				// ----> 配置并调用refresh方法
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
		// ----> 记录在servletContext中
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

		ClassLoader ccl = Thread.currentThread().getContextClassLoader();
		if (ccl == ContextLoader.class.getClassLoader()) {
			currentContext = this.context;
		}
		else if (ccl != null) {
			currentContextPerThread.put(ccl, this.context);
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
					WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
		}
		if (logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
		}

		return this.context;
	}
	catch (RuntimeException ex) {
		logger.error("Context initialization failed", ex);
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
		throw ex;
	}
	catch (Error err) {
		logger.error("Context initialization failed", err);
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
		throw err;
	}
}

创建WebApplicationContext

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	// ServletContext没有定义的话,默认从ContextLoader.properties中获取...XmlWebApplicationContext
	Class<?> contextClass = determineContextClass(sc);
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
				"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
	}
	// 反射获取
	return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

初始化ApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		// The application context id is still set to its original default value
		// -> assign a more useful id based on available information
		String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
		if (idParam != null) {
			wac.setId(idParam);
		}
		else {
			// Generate default id...
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(sc.getContextPath()));
		}
	}
	// 设置ServletContext
	wac.setServletContext(sc);
	// 设置配置文件路径
	String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
	if (configLocationParam != null) {
		wac.setConfigLocation(configLocationParam);
	}

	// The wac environment's #initPropertySources will be called in any case when the context
	// is refreshed; do it eagerly here to ensure servlet property sources are in place for
	// use in any post-processing or initialization that occurs below prior to #refresh
	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
	}
	// 初始化一些ServletContext上的初始化类(globalInitializerClasses,contextInitializerClasses)
	customizeContext(sc, wac);
	// ----> 调用refresh方法: IOC
	wac.refresh();
}

DispatcherServlet初始化

dispatcherServlet

首先是初始化,在其父类HttpServletBean中定义了init方法

public final void init() throws ServletException {
	if (logger.isDebugEnabled()) {
		logger.debug("Initializing servlet '" + getServletName() + "'");
	}

	// Set bean properties from init parameters.
	// 解析init-param并封装在pvs中
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			// 将当前的这个servlet类转换为一个BeanWrapper,从而能够以spring的方式来对init-param的值进行注入
			// 比如contextAttribute、contextClass、nameSpace、contextConfigLocation等
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			// 注册一个自定义属性编辑器,一旦遇到Resource类型的属性将会使用ResourceEditor进行解析
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			// 留给子类实现,空方法
			initBeanWrapper(bw);
			// 属性注入
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			}
			throw ex;
		}
	}

	// Let subclasses do whatever initialization they like.
	// ----> 1. 留给子类扩展,FrameworkServlet重写了这个方法
	initServletBean();

	if (logger.isDebugEnabled()) {
		logger.debug("Servlet '" + getServletName() + "' configured successfully");
	}
}

1 . servletBean初始化

protected final void initServletBean() throws ServletException {
	getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
	if (this.logger.isInfoEnabled()) {
		this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
	}
	long startTime = System.currentTimeMillis();

	try {
		// 初始化WebApplicationContext
		this.webApplicationContext = initWebApplicationContext();
		// 留给子类去实现,空方法
		initFrameworkServlet();
	}
	catch (ServletException | RuntimeException ex) {
		this.logger.error("Context initialization failed", ex);
		throw ex;
	}

	if (this.logger.isInfoEnabled()) {
		long elapsedTime = System.currentTimeMillis() - startTime;
		this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
				elapsedTime + " ms");
	}
}

2 . WebApplicationContext初始化

protected WebApplicationContext initWebApplicationContext() {
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;

	if (this.webApplicationContext != null) {
		// A context instance was injected at construction time -> use it
		// WebApplicationContext通过构造方法中被注入
		// 就比如上面说的,用java方式配置,会将WebApplicationContext通过DispatcherServlet构造方法设入
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				// The context has not yet been refreshed -> provide services such as
				// setting the parent context, setting the application context id, etc
				if (cwac.getParent() == null) {
					// The context instance was injected without an explicit parent -> set
					// the root application context (if any; may be null) as the parent
					cwac.setParent(rootContext);
				}
				// 刷新上下文环境
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	if (wac == null) {
		// No context instance was injected at construction time -> see if one
		// has been registered in the servlet context. If one exists, it is assumed
		// that the parent context (if any) has already been set and that the
		// user has performed any initialization such as setting the context id
		// 尝试通过contextAttribute属性去加载WebApplicationContext
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		// No context instance is defined for this servlet -> create a local one
		// 3. ----> 重新创建WebApplicationContext实例
		wac = createWebApplicationContext(rootContext);
	}

	// 判断refreshEventReceived,如果被Listener处理了,就不会进入
	if (!this.refreshEventReceived) {
		// Either the context is not a ConfigurableApplicationContext with refresh
		// support or the context injected at construction time had already been
		// refreshed -> trigger initial onRefresh manually here.
		// ----> 5. 留给子类扩展,DispatcherServlet重写了这个方法
		onRefresh(wac);
	}

	if (this.publishContext) {
		// Publish the context as a servlet context attribute.
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
					"' as ServletContext attribute with name [" + attrName + "]");
		}
	}

	return wac;
}

3 . 创建WebApplicationContext实例

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
	return createWebApplicationContext((ApplicationContext) parent);
}

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
	// 默认为XmlWebApplicationContext
	Class<?> contextClass = getContextClass();
	if (this.logger.isDebugEnabled()) {
		this.logger.debug("Servlet with name '" + getServletName() +
				"' will try to create custom WebApplicationContext context of class '" +
				contextClass.getName() + "'" + ", using parent context [" + parent + "]");
	}
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException(
				"Fatal initialization error in servlet with name '" + getServletName() +
				"': custom WebApplicationContext class [" + contextClass.getName() +
				"] is not of type ConfigurableWebApplicationContext");
	}
	// 反射实例化contextClass
	ConfigurableWebApplicationContext wac =
			(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	
	wac.setEnvironment(getEnvironment());
	// parent为在ContextLoaderListener中创建的父上下文
	wac.setParent(parent);
	// 获取contextConfigLoaction属性,配置在servlet初始化参数中
	String configLocation = getContextConfigLocation();
	if (configLocation != null) {
		wac.setConfigLocation(configLocation);
	}
	// ----> 4. 刷新上下文环境
	configureAndRefreshWebApplicationContext(wac);

	return wac;
}

4 . 刷新上下文环境,其实最后就还是调用父类AbstractApplicationContext的refresh进行IOC

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		// The application context id is still set to its original default value
		// -> assign a more useful id based on available information
		if (this.contextId != null) {
			wac.setId(this.contextId);
		}
		else {
			// Generate default id...
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
		}
	}

	wac.setServletContext(getServletContext());
	wac.setServletConfig(getServletConfig());
	wac.setNamespace(getNamespace());
	// 加入ContextRefreshListener,在IOC完成后会被事件通知,执行刷新方法
	wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

	// The wac environment's #initPropertySources will be called in any case when the context
	// is refreshed; do it eagerly here to ensure servlet property sources are in place for
	// use in any post-processing or initialization that occurs below prior to #refresh
	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
	}

	postProcessWebApplicationContext(wac);
	applyInitializers(wac);
	// ----> AbstractApplicationContext.refresh
	wac.refresh();
}

5 . 刷新

在AbstractApplicationContext.refresh方法的最后,finishRefresh方法中,会进行publishEvent操作,configureAndRefreshWebApplicationContext方法时被加入的ContextRefreshListener会被通知到执行onApplicationEvent方法

// FrameworkServlet$ContextRefreshListener
public void onApplicationEvent(ContextRefreshedEvent event) {
	FrameworkServlet.this.onApplicationEvent(event);
}

public void onApplicationEvent(ContextRefreshedEvent event) {
	this.refreshEventReceived = true;
	// ----> 就是调用这个刷新方法
	onRefresh(event.getApplicationContext());
}


// DispatcherServlet
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
	// 初始化MultipartResolver,主要用来处理文件上传,默认spring是没有的,需要在配置中添加Multipart解析器
	// 每个请求就会被监察是否包含Multipart,如果包含就会让定义的MultipartResolver去解析它
	initMultipartResolver(context);
	// 初始化LocaleResolver,主要用于国际化,通过配置的localeResolver来初始化
	initLocaleResolver(context);
	// 初始化ThemeResolver,主要用于主题资源,spring主题需要通过ThemeSoruce接口来实现存放主题信息的资源
	initThemeResolver(context);
	// ----> 6. 初始化handlerMappings
	initHandlerMappings(context);
	// ----> 7. 初始化HandlerAdapters
	initHandlerAdapters(context);
	// 初始化HandlerExceptionResolvers,用于异常处理
	// annotation-driven标签注册了ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver,DefaultHandlerExceptionResolver
	initHandlerExceptionResolvers(context);
	// 初始化RequestToViewNameTranslator,当controller没有返回一个view对象或逻辑视图名称,且没有向response写数据时,
	// spring会采用约定好的方式提供一个逻辑视图名称,这个是通过viewNameTranslator配置的,否则使用默认的DefaultRequestToViewNameTranslator
	initRequestToViewNameTranslator(context);
	// 初始化ViewResolvers,主要用于在返回结果后,选择适合的视图进行渲染,
	// 可以配置多个,使用视图解析器链,按优先级顺序查找
	initViewResolvers(context);
	// 初始化FlashMapManager,提供了一个请求存储属性,供其他请求使用,在使用重定向时非常重要
	initFlashMapManager(context);
}

6 . 初始化handlerMappings

客户端发出请求后,DispatcherServlet会将request交给HandlerMapping,然后HandlerMapping根据WebApplicationContext的配置回传给DispatcherServlet相应的Controller。我们可以给DispatcherServlet提供多个HandlerMapping,在选用过程中,根据指定的优先级进行排序,如果当前HandlerMapping能返回可用的Handler,就不再继续向下询问其他HandlerMapping,否则按优先级对各个HandlerMapping询问直到获取到可用的Handler

handlerMapping annotation-driven标签会注册AnnotationDrivenBeanDefinitionParser,里面定义了RequestMappingHandlerMapping,BeanNameUrlHandlerMapping,default-servlet-handler标签注册的DefaultServletHandlerBeanDefinitionParser里定义了SimpleUrlHandlerMapping

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.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.
	if (this.handlerMappings == null) {
		// 从DispatcherServlet.properties配置文件中获取
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isDebugEnabled()) {
			logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
		}
	}
}

7 . 初始化HandlerAdapters

DispatcherServlet通过HandlerMapping得到处理器后,会轮循处理器适配器模块,查找能够处理当前HTTP请求的HandlerAdapter的实现,处理器适配器模块根据HandlerMapping返回的处理器类型,来选择一个适当的HandlerMapping的实现,从而适配当前的HTTP请求 handlerAdapter annotation-driven标签注册了RequestMappingHandlerAdapter,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter

private void initHandlerAdapters(ApplicationContext context) {
	this.handlerAdapters = null;

	if (this.detectAllHandlerAdapters) {
		// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
		Map<String, HandlerAdapter> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerAdapters = new ArrayList<>(matchingBeans.values());
			// We keep HandlerAdapters in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerAdapters);
		}
	}
	else {
		try {
			HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
			this.handlerAdapters = Collections.singletonList(ha);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerAdapter later.
		}
	}

	// Ensure we have at least some HandlerAdapters, by registering
	// default HandlerAdapters if no other adapters are found.
	if (this.handlerAdapters == null) {
		// 从DispatcherServlet.properties配置文件中获取
		this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
		if (logger.isDebugEnabled()) {
			logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
		}
	}
}