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>
注: 不使用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初始化
首先是初始化,在其父类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
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请求 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");
}
}
}