SpringMVC源码解析
配置文件
分析SpringMVC之前,首先要看一下配置文件,因为它是我们分析SpringMVC源码的入口。其实里面的重点就是配置了一个ServletContext监听器,他的作用就是监听ServletContext的启动,并且调用重写的contextInitialized方法,这里将会是源码分析的开始。然后就是配置了一个DispatchServlet类型的Servlet,这个大家都熟悉,就是SpringMVC用来处理请求分发和渲染视图的一个Servlet。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<welcome-file-list>
<welcome-file>login.do</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcher-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
WepApplicationContext的创建
当我们在web.xml里面配置某个我们实现的了ServletContextListener接口的监听器后,就会监听ServletContext的启动,在ServletContext启动的时候,就会回调重写的contextInitialized。SpringMVC正是通过这个方法,开始WebApplicationContext的创建(现在还不知道要创建的WebApplicationContext具体是什么类型)
org.springframework.web.context.ContextLoaderListener#contextInitialized
@Override
public void contextInitialized(ServletContextEvent event) {
/**
* web.xml配置文件中配置了ContextLoaderListener
* 他是ServletContextListener的实现了
* 配置了监听器,Servlet容器启动时回先执行他的contextInitialized
* 然后才回去执行load on startup的Servlet的init方法
*/
initWebApplicationContext(event.getServletContext());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
org.springframework.web.context.ContextLoader#initWebApplicationContext
/** * 为给定的servlet context初始化Spring的WebApplicationContext, * 使用构成器提供的WebApplicationContext, * 或者根据 "contextClass" * 和 "contextConfigLocation" 两个context-params * 创建一个新的 */ public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { ...省略不重要的代码 try { // 创建WebApplicationContext // 存储WebApplicationContext到本地实例变量, // 保证他在ServletContext关闭的时候也是可用的 // 当然,同时也会保存一份到servletContext中 if (this.context == null) { // 在这里就会创建WebApplicationContext并返回 this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { if (cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } /*这里会手动执行WebApplicationContext的refresh方法*/ configureAndRefreshWebApplicationContext(cwac, servletContext); } } /* 存储WebApplicationContext到servletContext中 */ servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码 <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
org.springframework.web.context.ContextLoader#createWebApplicationContext
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
/*获取WebApplicationContext的具体类型 */
Class<?> contextClass = determineContextClass(sc);
...省略不重要的代码
/*通过返回的Class对象,反射实例化WebApplicationContext并且强转成ConfigurableWebApplicationContext返回*/
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这里就是确定具体的WebApplicationContext类型的地方
org.springframework.web.context.ContextLoader#determineContextClass
protected Class<?> determineContextClass(ServletContext servletContext) {
/*如果web.xml中指定了初始化参数ContextClass,就尝试获取此Class*/
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
/*获取到了,则返回contextClassName对应的WebApplicationContext的Class对象*/
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
/*没有指定WebApplicationContext类型,则从defaultStrategies中获取默认的,defaultStrategies就是一个Properties的对象*/
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
/*返回默认的WebApplicationContext的Class对象 */
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
可以看到首先会看看有没有在web.xml中指定初始化化参数ContextClass,这个参数需要指定具体的WebApplicationContext的完整类名,然后根据它加载对应的Class对象。但是我们一般不指定这东西,就是使用默认的,默认就会从Properties类型的对象defaultStrategies当中获取,而defaultStrategies其实是在ContextLoader的静态块中创建
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Properties defaultStrategies<span class="token punctuation">;</span> <span class="token keyword">static</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">/*在这里就会读取到配置文件ContextLoader.properties中默认的WebApplicationContext类型,并且保存到defaultStrategies中*/</span> ClassPathResource resource <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassPathResource</span><span class="token punctuation">(</span>DEFAULT_STRATEGIES_PATH<span class="token punctuation">,</span> ContextLoader<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> defaultStrategies <span class="token operator">=</span> PropertiesLoaderUtils<span class="token punctuation">.</span><span class="token function">loadProperties</span><span class="token punctuation">(</span>resource<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalStateException</span><span class="token punctuation">(</span><span class="token string">"Could not load 'ContextLoader.properties': "</span> <span class="token operator">+</span> ex<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
可以看到这里是读取配置文件ContextLoader.properties中配置的文件
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
- 1
- 2
- 3
- 4
- 5
可以看到,默认的类型就是XmlWebApplicationContext。通过返回XmlWebApplicationContext的Class对象,反射实例化XmlWebApplicationContext。
创建WebApplicationContext成功后,就会手动执行刷新,就是上面ContextLoader类的initWebApplicationContext中的configureAndRefreshWebApplicationContext(cwac, servletContext);。其实就是执行AbstractApplicationContext中的refresh方法,他是Spring中在ApplicationContext创建的时候,进行BeanDefinition的加载,BeanFactory创建,Bean的创建和保存等等一系列动作的入口。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { ...省略不重要的代码 /* 把ServletContext设置到WebApplicationContext中 */ wac.setServletContext(sc);
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码 <span class="token comment">/*执行WebApplicationContext的refresh方法,该方法继承于父类AbstractApplicationContext*/</span> wac<span class="token punctuation">.</span><span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
DispatchServlet的初始化
然后就是DispatchServlet的创建,因为web.xml中配置了load-on-startup为1,所以Servlet容器启动后马上启动。Servlet创建后会调用init初始化方法,而DispatchServlet的init的方法在他间接继承的父类HttpServletBean中
org.springframework.web.servlet.HttpServletBean#init
/** * 将配置参数映射到这个servlet的bean属性上,并调用子类初始化 */ @Override public final void init() throws ServletException { // 从init参数设置bean属性. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); 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; } }
<span class="token comment">// 子类的初始化工作.</span> <span class="token function">initServletBean</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
org.springframework.web.servlet.FrameworkServlet#initServletBean
@Override
protected final void initServletBean() throws ServletException {
...省略不重要的代码
try {
/**
* 初始化WebApplicationContext, 并保存到DispatchServlet的成员变量中
* 这里之所以要这样做,是因为DispatchServlet重载类构造方法,支持通过构造方法参入一个WebApplicationContext
* 另外之前创建的WebApplicationContext,是保存到ContextLoader的成员变量和ServletContext中的
* DispatchServlet中并没有
*/
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
...省略不重要的代码
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() { /*ServletContext中获取WebApplicationContext,就是ContextLoader中创建的WebApplicationContext*/ WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 这里要判断一次啊DispatchServlet中的成员变量webApplicationContext // 因为DispatchServlet重载的构造,支持传入WebApplicationContext类型的参数 if (this.webApplicationContext != null) { // 在构造器注入上下文实例,使用它 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // 上下文尚未刷新 -> 提供诸如设置父上下文、设置应用程序上下文id等服务 if (cwac.getParent() == null) { // 如果当前的WebApplicationContext对象的parent属性为空,则赋值为rootContext cwac.setParent(rootContext); } /*手动refresh当前的WebApplicationXml,和前面一样,因为是构造方法传入的,还没用refresh */ configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // 在构建时没有注入上下文实例,看看是否已经在servlet上下文中注册了一个。 // 如果存在,则假定父上下文(如果有的话)已经设置, // 并且用户已经执行了任何初始化,比如设置上下文id // 其实就是从ServletContext找到ContextLoader创建的WebApplicationContext wac = findWebApplicationContext(); } if (wac == null) { // 没有为这个servlet定义上下文实例->创建一个 wac = createWebApplicationContext(rootContext); }
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>refreshEventReceived<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 要么上下文不是具有刷新支持的可配置applicationcontext,</span> <span class="token comment">// 要么在构建时注入的上下文已经被刷新</span> <span class="token comment">// 在这里手动触发初始化onRefresh方法</span> <span class="token comment">// 会调用到DispatchServlet重写的方法</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>onRefreshMonitor<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 接下来就是分析这里</span> <span class="token function">onRefresh</span><span class="token punctuation">(</span>wac<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>publishContext<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 将WebApplicationContext作为servlet上下文属性发布。</span> String attrName <span class="token operator">=</span> <span class="token function">getServletContextAttributeName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">getServletContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span>attrName<span class="token punctuation">,</span> wac<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> wac<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
org.springframework.web.servlet.DispatcherServlet#onRefresh
/** * 这个实现调用{@link #initStrategies}。 */ @Override protected void onRefresh(ApplicationContext context) { initStrategies(context); }
<span class="token comment">/** * 初始化这个servlet使用的策略对象。 * 可以在子类中重写,以便进一步初始化策略对象。 */</span> <span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">initStrategies</span><span class="token punctuation">(</span>ApplicationContext context<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">initMultipartResolver</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">initLocaleResolver</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">initThemeResolver</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 初始化HandlerMappings */</span> <span class="token function">initHandlerMappings</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 初始化HandlerAdapters */</span> <span class="token function">initHandlerAdapters</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">initHandlerExceptionResolvers</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">initRequestToViewNameTranslator</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 初始化ViewResolvers */</span> <span class="token function">initViewResolvers</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">initFlashMapManager</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
可以看到,接下来就是初始化DispatchServlet中的各种组件了,比较重要的有HandlerMappings,HandlerAdapters,ViewResolvers。
DispatchServlet重要组件的初始化
org.springframework.web.servlet.DispatcherServlet#initHandlerMappings
/** * 初始化该类使用的处理器映射。 * 如果在这个名称空间的BeanFactory中没有定义HandlerMapping bean, * 我们默认使用BeanNameUrlHandlerMapping。 */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; /*detectAllHandlerMappings 默认true*/ if (this.detectAllHandlerMappings) { // 找到所有ApplicationContext中的HandlerMapping,包括祖先ApplicationContext Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { /*保存所有的HandlerMapping*/ this.handlerMappings = new ArrayList<>(matchingBeans.values()); // 对HandlerMapping进行排序. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { /** * 默然情况下,SpringMVC会加载所有实现了HandlerMapping接口的Bean, * 但是也可以让SpringMVC只加载BeanName为handlerMapping的HandlerMapping实现类, * 就是在web.xml配置文件中配置初始化化参数detectAllHandlerMappings为false * 如果没有找到BeanName为handlerMapping的Bean, * 则加载DispatcherServlet.properties配置文件中默认的handlerMappings */ 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. } }
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码 <span class="token keyword">for</span> <span class="token punctuation">(</span>HandlerMapping mapping <span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handlerMappings<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mapping<span class="token punctuation">.</span><span class="token function">usesPathPatterns</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">this</span><span class="token punctuation">.</span>parseRequestPath <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters
/** * 初始化该类使用的handleradapter。 * 如果在此名称空间的BeanFactory中没有定义HandlerAdapter bean, * 我们默认为SimpleControllerHandlerAdapter。 */ private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; /*detectAllHandlerAdapters 默认true*/ if (this.detectAllHandlerAdapters) { // 找到所有ApplicationContext中的HandlerAdapter,包括祖先ApplicationContext. Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { /*保存所有的HandlerAdapter*/ this.handlerAdapters = new ArrayList<>(matchingBeans.values()); // 对HandlerAdapter进行排序.. AnnotationAwareOrderComparator.sort(this.handlerAdapters); } } else { /** * 如果在web.xml中初始化参数detectAllHandlerAdapters为false, * 则只在WebApplicationContext中查找名为handlerAdapter的Bean * 如果没有,则使用DispatcherServlet.properties配置文件中默认的的HandlerAdapter */ 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. } }
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码 <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
org.springframework.web.servlet.DispatcherServlet#initViewResolvers
/**
* 初始化该类使用的ViewResolvers。
* 如果在BeanFactory中没有为此定义ViewResolver bean
* 名称空间,默认为InternalResourceViewResolver。
*/
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
/*detectAllViewResolvers 默认true*/
if (this.detectAllViewResolvers) {
// 找到所有ApplicationContext中的ViewResolver,包括祖先ApplicationContext.
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
/*保存所有的ViewResolver*/
this.viewResolvers = new ArrayList<>(matchingBeans.values());
// 对ViewResolver进行排序
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
/**
* 如果在web.xml中初始化参数detectAllViewResolvers为false,
* 则只在WebApplicationContext中查找名为viewResolver的Bean
* 如果没有,则使用DispatcherServlet.properties配置文件中默认的的ViewResolver
*/
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default ViewResolver later.
}
}
...省略不重要的代码
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
DispatchServlet的组件全部初始化完毕后,一切的准备工作就都做好了,接下来就可以正常的处理请求。在此之前先贴上spring-webmvc指定默认组件类型的properties文件DispatcherServlet.properties
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
DispatchServlet的处理逻辑
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
<span class="token function">processRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * Delegate POST requests to {@link #processRequest}. * @see #doService */</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">protected</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">doPost</span><span class="token punctuation">(</span>HttpServletRequest request<span class="token punctuation">,</span> HttpServletResponse response<span class="token punctuation">)</span> <span class="token keyword">throws</span> ServletException<span class="token punctuation">,</span> IOException <span class="token punctuation">{<!-- --></span> <span class="token function">processRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
可以看到,无论是get请求,或者是post请求,都调用的是processRequest方法来处理请求
org.springframework.web.servlet.FrameworkServlet#processRequest
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
...省略不重要的代码
doService(request, response);
...省略不重要的代码
}
- 1
- 2
- 3
- 4
- 5
- 6
org.springframework.web.servlet.DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
/*doDispatch中就是完整的请求处理过程*/
doDispatch(request, response);
}
- 1
- 2
- 3
- 4
下面就是DispatchServlet真正处理请求的过程了,在继续往下看前,先贴一张DispatchServlet处理请求的完整流程图
然后对照代码看一下,是否流程完全一样?
org.springframework.web.servlet.DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false;
WebAsyncManager asyncManager <span class="token operator">=</span> WebAsyncUtils<span class="token punctuation">.</span><span class="token function">getAsyncManager</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> ModelAndView mv <span class="token operator">=</span> null<span class="token punctuation">;</span> Exception dispatchException <span class="token operator">=</span> null<span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> processedRequest <span class="token operator">=</span> <span class="token function">checkMultipart</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span> multipartRequestParsed <span class="token operator">=</span> <span class="token punctuation">(</span>processedRequest <span class="token operator">!=</span> request<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/*根据request寻找对应的mappedHandler,然后获取HandlerExecutionChain*/</span> mappedHandler <span class="token operator">=</span> <span class="token function">getHandler</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mappedHandler <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">/*如果没有找到对应的Handler,则通过response返回错误信息*/</span> <span class="token function">noHandlerFound</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*根据当前的mappedHandler的getHandler方法返回的handler(一般就是HandlerMethod,老式用法就是Controller),寻找对应的HandlerAdapter*/</span> HandlerAdapter ha <span class="token operator">=</span> <span class="token function">getHandlerAdapter</span><span class="token punctuation">(</span>mappedHandler<span class="token punctuation">.</span><span class="token function">getHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Process last-modified header, if supported by the handler.</span> String method <span class="token operator">=</span> request<span class="token punctuation">.</span><span class="token function">getMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">boolean</span> isGet <span class="token operator">=</span> <span class="token string">"GET"</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>isGet <span class="token operator">||</span> <span class="token string">"HEAD"</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">long</span> lastModified <span class="token operator">=</span> ha<span class="token punctuation">.</span><span class="token function">getLastModified</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">.</span><span class="token function">getHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ServletWebRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">checkNotModified</span><span class="token punctuation">(</span>lastModified<span class="token punctuation">)</span> <span class="token operator">&&</span> isGet<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">/** * 调用拦截器链上,各拦截器的preHandle方法 * 如果其中一个返回false, * 就执行拦截器链所有的拦截器的afterCompletion方法 * 然后返回false,然后这里就会return * 正常情况下不进这个分支 */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mappedHandler<span class="token punctuation">.</span><span class="token function">applyPreHandle</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 处理请求并返回ModelAndView对象.</span> mv <span class="token operator">=</span> ha<span class="token punctuation">.</span><span class="token function">handle</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">.</span><span class="token function">getHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>asyncManager<span class="token punctuation">.</span><span class="token function">isConcurrentHandlingStarted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*如果ModelAndView中没有View,则添加默认的viewName*/</span> <span class="token function">applyDefaultViewName</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> mv<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/*调用各拦截器链上拦截器的postHandler方法*/</span> mappedHandler<span class="token punctuation">.</span><span class="token function">applyPostHandle</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mv<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> dispatchException <span class="token operator">=</span> ex<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> err<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// As of 4.3, we're processing Errors thrown from handler methods as well,</span> <span class="token comment">// making them available for @ExceptionHandler methods and other scenarios.</span> dispatchException <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">NestedServletException</span><span class="token punctuation">(</span><span class="token string">"Handler dispatch failed"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*处理ModelAndView到View,再渲染jsp,通过response输出页面的过程。 当然最后还会调用拦截器的afterCompletion方法 */</span> <span class="token function">processDispatchResult</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">,</span> mv<span class="token punctuation">,</span> dispatchException<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">triggerAfterCompletion</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> err<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">triggerAfterCompletion</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">,</span> mappedHandler<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">NestedServletException</span><span class="token punctuation">(</span><span class="token string">"Handler processing failed"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>asyncManager<span class="token punctuation">.</span><span class="token function">isConcurrentHandlingStarted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Instead of postHandle and afterCompletion</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mappedHandler <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> mappedHandler<span class="token punctuation">.</span><span class="token function">applyAfterConcurrentHandlingStarted</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Clean up any resources used by a multipart request.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>multipartRequestParsed<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">cleanupMultipart</span><span class="token punctuation">(</span>processedRequest<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
获取mappedHandler
这里会遍历DispatchSerlet中所有的HandlerMapping,尝试通过request查找对应的handler。handler有可能有不同类型,如果我们平常的那种注解式开发的Controller,就会返回HandlerMethod。但是还用一种方式,就是我们编写的Controller类继承AbstractController,那么这里就会返回Controller。然后把handler和拦截器封装成一个HandlerExecutionChain对象
org.springframework.web.servlet.DispatcherServlet#getHandler
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/*遍历所有的HandlerMapping,直到通过request可以获取到HandlerExecutionChain */
for (HandlerMapping mapping : this.handlerMappings) {
/*返回的是HandlerExecutionChain对象,不是想象中的一个HandlerMapping,更不是直接返回handler*/
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 根据request对象获取handler,其实就是根据url获取对应的HandlerMethod Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } /*把HandlerMethod封装成HandlerExecutionChain,里面包含了各个拦截器*/ HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码 <span class="token keyword">return</span> executionChain<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
下面就是根据request中的url匹配到对应的HandlerMethode的过程
/**
* 查找给定请求的URL路径的handler.
* @param request current HTTP request
* @return the handler instance, or {@code null} if none found
*/
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = initLookupPath(request);
Object handler;
if (usesPathPatterns()) {
RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
handler = lookupHandler(path, lookupPath, request);
}
else {
handler = lookupHandler(lookupPath, request);
}
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
然后把获取到的HandlerMethod封装成HandlerExecutionChain,其实就是添加上各种拦截器
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
<span class="token keyword">for</span> <span class="token punctuation">(</span>HandlerInterceptor interceptor <span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>adaptedInterceptors<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>interceptor <span class="token keyword">instanceof</span> <span class="token class-name">MappedInterceptor</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> MappedInterceptor mappedInterceptor <span class="token operator">=</span> <span class="token punctuation">(</span>MappedInterceptor<span class="token punctuation">)</span> interceptor<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mappedInterceptor<span class="token punctuation">.</span><span class="token function">matches</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> chain<span class="token punctuation">.</span><span class="token function">addInterceptor</span><span class="token punctuation">(</span>mappedInterceptor<span class="token punctuation">.</span><span class="token function">getInterceptor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> chain<span class="token punctuation">.</span><span class="token function">addInterceptor</span><span class="token punctuation">(</span>interceptor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> chain<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
返回的是一个HandlerExecutionChain 对象,然后下面就是HandlerExecutionChain 的成员变量
public class HandlerExecutionChain { /*真正的handler,就是HandlerMethod或者Controller*/ private final Object handler;
<span class="token comment">/*存放拦截器的数组*/</span> <span class="token annotation punctuation">@Nullable</span> <span class="token keyword">private</span> HandlerInterceptor<span class="token punctuation">[</span><span class="token punctuation">]</span> interceptors<span class="token punctuation">;</span> <span class="token comment">/*存放拦截器的list*/</span> <span class="token annotation punctuation">@Nullable</span> <span class="token keyword">private</span> List<span class="token generics function"><span class="token punctuation"><</span>HandlerInterceptor<span class="token punctuation">></span></span> interceptorList<span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
顺便贴一下HandlerMethod中关键的成员变量
public class HandlerMethod { // 这个就是我们打了@Controller标签的那个类 private final Object bean;
<span class="token annotation punctuation">@Nullable</span> <span class="token keyword">private</span> <span class="token keyword">final</span> BeanFactory beanFactory<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> beanType<span class="token punctuation">;</span> <span class="token comment">// 这个是他要执行的方法</span> <span class="token keyword">private</span> <span class="token keyword">final</span> Method method<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> Method bridgedMethod<span class="token punctuation">;</span> <span class="token comment">// 方法参数</span> <span class="token keyword">private</span> <span class="token keyword">final</span> MethodParameter<span class="token punctuation">[</span><span class="token punctuation">]</span> parameters<span class="token punctuation">;</span> <span class="token annotation punctuation">@Nullable</span> <span class="token keyword">private</span> HttpStatus responseStatus<span class="token punctuation">;</span> <span class="token annotation punctuation">@Nullable</span> <span class="token keyword">private</span> String responseStatusReason<span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
根据mappedHandler中的handler对象,获取HandlerAdapter
因为有不同的handler,不同的handler会有不同的接口,所有需要HandlerAdapter去做适配,这里用到的就是设计模式中的适配器模式。拿到了返回的HandlerExecutionChain 对象之后,就会通过里面的handler,获取HandlerAdapter。
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
/*遍历所有的HandlerAdapter,直到该HandlerAdapter的supports方法返回true*/
for (HandlerAdapter adapter : this.handlerAdapters) {
/*如果adapter的supports方法返回true,代表就是要找的handler*/
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
如果handler的类型是HandlerMethoded的话
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
- 1
- 2
- 3
- 4
或者handler的类型是老式用法的继承Controller
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#supports
@Override
public boolean supports(Object handler) {
/*当前handler对象时Controller类型,就返回*/
return (handler instanceof Controller);
}
- 1
- 2
- 3
- 4
- 5
处理请求并返回ModelAndView对象
先贴一行DispatchServlet中走到的当前的步骤
org.springframework.web.servlet.DispatcherServlet#doDispatch
// 处理请求并返回ModelAndView对象
// ha就是上面寻找到的HandlerAdapter,mappedHandler.getHandler()返回的是HandlerMethod或者Controller.
// 下面就只分析HandlerMethod的了
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- 1
- 2
- 3
- 4
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
@Override @Nullable public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
<span class="token keyword">return</span> <span class="token function">handleInternal</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> <span class="token punctuation">(</span>HandlerMethod<span class="token punctuation">)</span> handler<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
下面就是处理请求返回ModelAndView的过程了
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav<span class="token punctuation">;</span> <span class="token comment">// 要返回的ModelAndView</span> <span class="token function">checkRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Execute invokeHandlerMethod in synchronized block if required.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>synchronizeOnSession<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> HttpSession session <span class="token operator">=</span> request<span class="token punctuation">.</span><span class="token function">getSession</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>session <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Object mutex <span class="token operator">=</span> WebUtils<span class="token punctuation">.</span><span class="token function">getSessionMutex</span><span class="token punctuation">(</span>session<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span>mutex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 执行HandlerMethod中的方法,返回ModelAndView</span> mav <span class="token operator">=</span> <span class="token function">invokeHandlerMethod</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> handlerMethod<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 执行HandlerMethod中的方法,返回ModelAndView</span> mav <span class="token operator">=</span> <span class="token function">invokeHandlerMethod</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> handlerMethod<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 执行HandlerMethod中的方法,返回ModelAndView</span> mav <span class="token operator">=</span> <span class="token function">invokeHandlerMethod</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> handlerMethod<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码 <span class="token keyword">return</span> mav<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { /*封装请求参数webRequest*/ ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); /*根据当前的HandlerMethod创建HandlerMethod的间接子类ServletInvocableHandlerMethod*/ ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
<span class="token comment">/*ModelAndViewContainer 意思就是ModelAndView的容器*/</span> ModelAndViewContainer mavContainer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ModelAndViewContainer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> mavContainer<span class="token punctuation">.</span><span class="token function">addAllAttributes</span><span class="token punctuation">(</span>RequestContextUtils<span class="token punctuation">.</span><span class="token function">getInputFlashMap</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> modelFactory<span class="token punctuation">.</span><span class="token function">initModel</span><span class="token punctuation">(</span>webRequest<span class="token punctuation">,</span> mavContainer<span class="token punctuation">,</span> invocableMethod<span class="token punctuation">)</span><span class="token punctuation">;</span> mavContainer<span class="token punctuation">.</span><span class="token function">setIgnoreDefaultModelOnRedirect</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>ignoreDefaultModelOnRedirect<span class="token punctuation">)</span><span class="token punctuation">;</span> AsyncWebRequest asyncWebRequest <span class="token operator">=</span> WebAsyncUtils<span class="token punctuation">.</span><span class="token function">createAsyncWebRequest</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span> asyncWebRequest<span class="token punctuation">.</span><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>asyncRequestTimeout<span class="token punctuation">)</span><span class="token punctuation">;</span> WebAsyncManager asyncManager <span class="token operator">=</span> WebAsyncUtils<span class="token punctuation">.</span><span class="token function">getAsyncManager</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span> asyncManager<span class="token punctuation">.</span><span class="token function">setTaskExecutor</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>taskExecutor<span class="token punctuation">)</span><span class="token punctuation">;</span> asyncManager<span class="token punctuation">.</span><span class="token function">setAsyncWebRequest</span><span class="token punctuation">(</span>asyncWebRequest<span class="token punctuation">)</span><span class="token punctuation">;</span> asyncManager<span class="token punctuation">.</span><span class="token function">registerCallableInterceptors</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>callableInterceptors<span class="token punctuation">)</span><span class="token punctuation">;</span> asyncManager<span class="token punctuation">.</span><span class="token function">registerDeferredResultInterceptors</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>deferredResultInterceptors<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>asyncManager<span class="token punctuation">.</span><span class="token function">hasConcurrentResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Object result <span class="token operator">=</span> asyncManager<span class="token punctuation">.</span><span class="token function">getConcurrentResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> mavContainer <span class="token operator">=</span> <span class="token punctuation">(</span>ModelAndViewContainer<span class="token punctuation">)</span> asyncManager<span class="token punctuation">.</span><span class="token function">getConcurrentResultContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> asyncManager<span class="token punctuation">.</span><span class="token function">clearConcurrentResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> LogFormatUtils<span class="token punctuation">.</span><span class="token function">traceDebug</span><span class="token punctuation">(</span>logger<span class="token punctuation">,</span> traceOn <span class="token operator">-</span><span class="token operator">></span> <span class="token punctuation">{<!-- --></span> String formatted <span class="token operator">=</span> LogFormatUtils<span class="token punctuation">.</span><span class="token function">formatValue</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> <span class="token operator">!</span>traceOn<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token string">"Resume with async result ["</span> <span class="token operator">+</span> formatted <span class="token operator">+</span> <span class="token string">"]"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> invocableMethod <span class="token operator">=</span> invocableMethod<span class="token punctuation">.</span><span class="token function">wrapConcurrentResult</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*执行请求处理*/</span> invocableMethod<span class="token punctuation">.</span><span class="token function">invokeAndHandle</span><span class="token punctuation">(</span>webRequest<span class="token punctuation">,</span> mavContainer<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>asyncManager<span class="token punctuation">.</span><span class="token function">isConcurrentHandlingStarted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> null<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 获取并返回ModelAndView</span> <span class="token keyword">return</span> <span class="token function">getModelAndView</span><span class="token punctuation">(</span>mavContainer<span class="token punctuation">,</span> modelFactory<span class="token punctuation">,</span> webRequest<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{<!-- --></span> webRequest<span class="token punctuation">.</span><span class="token function">requestCompleted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
/*执行请求处理,继续点进去*/
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
...省略不重要代码
}
- 1
- 2
- 3
- 4
- 5
- 6
org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
/*获取方法的参数*/
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
/*里面是真的要执行了*/
return doInvoke(args);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
/*利用反射的方式执行方法,就是method.invoke(object, args)*/
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
...省略不重要代码
}
catch (InvocationTargetException ex) {
...省略不重要代码
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
请求已经处理完了,还有一步返回ModelAndView
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getModelAndView
@Nullable private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory<span class="token punctuation">.</span><span class="token function">updateModel</span><span class="token punctuation">(</span>webRequest<span class="token punctuation">,</span> mavContainer<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mavContainer<span class="token punctuation">.</span><span class="token function">isRequestHandled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> null<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*从ModelAndViewContainer中获取ModelMap*/</span> ModelMap model <span class="token operator">=</span> mavContainer<span class="token punctuation">.</span><span class="token function">getModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/*创建ModelAndView*/</span> ModelAndView mav <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ModelAndView</span><span class="token punctuation">(</span>mavContainer<span class="token punctuation">.</span><span class="token function">getViewName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> model<span class="token punctuation">,</span> mavContainer<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token comment">/*返回ModelAndView*/</span> <span class="token keyword">return</span> mav<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
viewResolver通过viewName获取view
现在已经处理完请求,返回了ModelAndView,回到DispatcServlet的doDispatch方法中,进行下一步的分析。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
/*处理ModelAndView到View,再渲染jsp,通过response输出页面的过程 */
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
...
}
- 1
- 2
- 3
- 4
- 5
- 6
org.springframework.web.servlet.DispatcherServlet#processDispatchResult
/** * 处理handler选择和handler调用的结果,该结果要么是ModelAndView,要么是要解析到ModelAndView的异常。 */ private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
<span class="token keyword">boolean</span> errorView <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token comment">/*发生异常的处理 */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exception <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exception <span class="token keyword">instanceof</span> <span class="token class-name">ModelAndViewDefiningException</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> logger<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"ModelAndViewDefiningException encountered"</span><span class="token punctuation">,</span> exception<span class="token punctuation">)</span><span class="token punctuation">;</span> mv <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>ModelAndViewDefiningException<span class="token punctuation">)</span> exception<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getModelAndView</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> Object handler <span class="token operator">=</span> <span class="token punctuation">(</span>mappedHandler <span class="token operator">!=</span> null <span class="token operator">?</span> mappedHandler<span class="token punctuation">.</span><span class="token function">getHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span> mv <span class="token operator">=</span> <span class="token function">processHandlerException</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> handler<span class="token punctuation">,</span> exception<span class="token punctuation">)</span><span class="token punctuation">;</span> errorView <span class="token operator">=</span> <span class="token punctuation">(</span>mv <span class="token operator">!=</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 处理器是否返回一个要渲染的view?</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mv <span class="token operator">!=</span> null <span class="token operator">&&</span> <span class="token operator">!</span>mv<span class="token punctuation">.</span><span class="token function">wasCleared</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">/*viewResolver通过ModelAndView获取View,进行渲染*/</span> <span class="token function">render</span><span class="token punctuation">(</span>mv<span class="token punctuation">,</span> request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>errorView<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> WebUtils<span class="token punctuation">.</span><span class="token function">clearErrorRequestAttributes</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>省略不重要的代码 <span class="token keyword">if</span> <span class="token punctuation">(</span>mappedHandler <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 调用该Handler上所有拦截器链中的afterCompletion方法</span> mappedHandler<span class="token punctuation">.</span><span class="token function">triggerAfterCompletion</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
在render方法中就会开始进行,里面将会遍历viewResolvers,尝试通过ModelAndView获取View对象。然后通过View对象的render方法,进行渲染工作。
org.springframework.web.servlet.DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale);
View view<span class="token punctuation">;</span> <span class="token comment">/*通过ModelAndView获取viewName*/</span> String viewName <span class="token operator">=</span> mv<span class="token punctuation">.</span><span class="token function">getViewName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>viewName <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// ModelAndView中包含viewName,则尝试通过viewResolver解析</span> view <span class="token operator">=</span> <span class="token function">resolveViewName</span><span class="token punctuation">(</span>viewName<span class="token punctuation">,</span> mv<span class="token punctuation">.</span><span class="token function">getModelInternal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> locale<span class="token punctuation">,</span> request<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>view <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ServletException</span><span class="token punctuation">(</span><span class="token string">"Could not resolve view with name '"</span> <span class="token operator">+</span> mv<span class="token punctuation">.</span><span class="token function">getViewName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"' in servlet with name '"</span> <span class="token operator">+</span> <span class="token function">getServletName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"'"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 不需要查找:ModelAndView对象包含实际的视图对象。</span> view <span class="token operator">=</span> mv<span class="token punctuation">.</span><span class="token function">getView</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>view <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ServletException</span><span class="token punctuation">(</span><span class="token string">"ModelAndView ["</span> <span class="token operator">+</span> mv <span class="token operator">+</span> <span class="token string">"] neither contains a view name nor a "</span> <span class="token operator">+</span> <span class="token string">"View object in servlet with name '"</span> <span class="token operator">+</span> <span class="token function">getServletName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"'"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 委托给view对象进行渲染。</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Rendering view ["</span> <span class="token operator">+</span> view <span class="token operator">+</span> <span class="token string">"] "</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mv<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> response<span class="token punctuation">.</span><span class="token function">setStatus</span><span class="token punctuation">(</span>mv<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*视图渲染(jsp),然后通过response返回渲染的页面*/</span> view<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span>mv<span class="token punctuation">.</span><span class="token function">getModelInternal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isDebugEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> logger<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Error rendering view ["</span> <span class="token operator">+</span> view <span class="token operator">+</span> <span class="token string">"]"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">throw</span> ex<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
这里就会遍历所有的viewResolver,都尝试获取View,如果获取到了就返回。
org.springframework.web.servlet.DispatcherServlet#resolveViewName
@Nullable protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>viewResolvers <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">/*遍历所有的viewResolver*/</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>ViewResolver viewResolver <span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>viewResolvers<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">/*尝试通过viewResolver解析viewName,看是否能返回view*/</span> View view <span class="token operator">=</span> viewResolver<span class="token punctuation">.</span><span class="token function">resolveViewName</span><span class="token punctuation">(</span>viewName<span class="token punctuation">,</span> locale<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/*获取都view不为空,则返回*/</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>view <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> view<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> null<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
这里会判断一下是否有缓存,有的话就尝试从缓存中取,没有的话直接创建
org.springframework.web.servlet.view.AbstractCachingViewResolver#resolveViewName
@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
/*不存在缓存的情况下直接创建view*/
return createView(viewName, locale);
}
else {
/*否则先尝试从缓存中取*/
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
}
}
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace(formatKey(cacheKey) + "served from cache");
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
我们直接看createView方法,因为当前viewResolver默认的类型是InternalResourceViewResolver,在上面的DispatcherServlet.properties中可以看到。而InternalResourceViewResolver又继承了UrlBasedViewResolver,createView方法在UrlBasedViewResolver中重写了,所以这里首先会调用类UrlBasedViewResolver的createView方法
org.springframework.web.servlet.view.UrlBasedViewResolver#createView
@Override protected View createView(String viewName, Locale locale) throws Exception { // 如果这个解析器不支持处理给定的view, // 返回null以传递给链中的下一个解析器 if (!canHandle(viewName, locale)) { return null; }
<span class="token comment">// "redirect:" 前缀,重定向的处理。</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>viewName<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span>REDIRECT_URL_PREFIX<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> String redirectUrl <span class="token operator">=</span> viewName<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>REDIRECT_URL_PREFIX<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> RedirectView view <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RedirectView</span><span class="token punctuation">(</span>redirectUrl<span class="token punctuation">,</span> <span class="token function">isRedirectContextRelative</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">isRedirectHttp10Compatible</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> String<span class="token punctuation">[</span><span class="token punctuation">]</span> hosts <span class="token operator">=</span> <span class="token function">getRedirectHosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>hosts <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> view<span class="token punctuation">.</span><span class="token function">setHosts</span><span class="token punctuation">(</span>hosts<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token function">applyLifecycleMethods</span><span class="token punctuation">(</span>REDIRECT_URL_PREFIX<span class="token punctuation">,</span> view<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// "forward:" 前缀,内部转发的处理。</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>viewName<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span>FORWARD_URL_PREFIX<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> String forwardUrl <span class="token operator">=</span> viewName<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>FORWARD_URL_PREFIX<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> InternalResourceView view <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">InternalResourceView</span><span class="token punctuation">(</span>forwardUrl<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">applyLifecycleMethods</span><span class="token punctuation">(</span>FORWARD_URL_PREFIX<span class="token punctuation">,</span> view<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 否则回到超类实现:调用loadView.</span> <span class="token keyword">return</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">createView</span><span class="token punctuation">(</span>viewName<span class="token punctuation">,</span> locale<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
org.springframework.web.servlet.view.AbstractCachingViewResolver#createView
这里除了处理重定向和转发的处理,剩下的就是回调父类AbstractCachingViewResolver的createView方法,然后里面又会调用loadView方法。
@Nullable
protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale);
}
- 1
- 2
- 3
- 4
UrlBasedViewResolver重写了loadView方法,所以又会回到UrlBasedViewResolver中,这里就会真正的创建View并返回
org.springframework.web.servlet.view.UrlBasedViewResolver#loadView
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
/* 通过viewName获取View的子类AbstractUrlBasedView */
AbstractUrlBasedView view = buildView(viewName);
/* 执行bean的初始化回调方法initializeBean */
View result = applyLifecycleMethods(viewName, view);
// 返回view
return (view.checkResource(locale) ? result : null);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
buildView方法会调用InternalResourceViewResolver的buildView,然后又会调用父类UrlBasedViewResolver的buildView方法
org.springframework.web.servlet.view.InternalResourceViewResolver#buildView
@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
/*调用UrlBasedViewResolver的buildView方法*/
InternalResourceView view = (InternalResourceView) super.buildView(viewName);
if (this.alwaysInclude != null) {
view.setAlwaysInclude(this.alwaysInclude);
}
view.setPreventDispatchLoop(true);
return view;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
buildView方法中会反射实例化一个AbstractUrlBasedView对象,然后对他进行一推的属性设置
org.springframework.web.servlet.view.UrlBasedViewResolver#buildView
protected AbstractUrlBasedView buildView(String viewName) throws Exception { Class<?> viewClass = getViewClass(); Assert.state(viewClass != null, "No view class");
AbstractUrlBasedView view <span class="token operator">=</span> <span class="token punctuation">(</span>AbstractUrlBasedView<span class="token punctuation">)</span> BeanUtils<span class="token punctuation">.</span><span class="token function">instantiateClass</span><span class="token punctuation">(</span>viewClass<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/*添加前缀以及后缀*/</span> view<span class="token punctuation">.</span><span class="token function">setUrl</span><span class="token punctuation">(</span><span class="token function">getPrefix</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> viewName <span class="token operator">+</span> <span class="token function">getSuffix</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> view<span class="token punctuation">.</span><span class="token function">setAttributesMap</span><span class="token punctuation">(</span><span class="token function">getAttributesMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> String contentType <span class="token operator">=</span> <span class="token function">getContentType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>contentType <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> view<span class="token punctuation">.</span><span class="token function">setContentType</span><span class="token punctuation">(</span>contentType<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> String requestContextAttribute <span class="token operator">=</span> <span class="token function">getRequestContextAttribute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>requestContextAttribute <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> view<span class="token punctuation">.</span><span class="token function">setRequestContextAttribute</span><span class="token punctuation">(</span>requestContextAttribute<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> Boolean exposePathVariables <span class="token operator">=</span> <span class="token function">getExposePathVariables</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exposePathVariables <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> view<span class="token punctuation">.</span><span class="token function">setExposePathVariables</span><span class="token punctuation">(</span>exposePathVariables<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> Boolean exposeContextBeansAsAttributes <span class="token operator">=</span> <span class="token function">getExposeContextBeansAsAttributes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exposeContextBeansAsAttributes <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> view<span class="token punctuation">.</span><span class="token function">setExposeContextBeansAsAttributes</span><span class="token punctuation">(</span>exposeContextBeansAsAttributes<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> String<span class="token punctuation">[</span><span class="token punctuation">]</span> exposedContextBeanNames <span class="token operator">=</span> <span class="token function">getExposedContextBeanNames</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exposedContextBeanNames <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> view<span class="token punctuation">.</span><span class="token function">setExposedContextBeanNames</span><span class="token punctuation">(</span>exposedContextBeanNames<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> view<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
最后就会返回这个view,然后调用他的render方法
view的渲染
先回到DispatchServlet的render方法,看看我们接下来要看的代码
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
/*视图渲染(jsp),然后通过response返回渲染的页面*/
view.render(mv.getModelInternal(), request, response);
}
- 1
- 2
- 3
- 4
org.springframework.web.servlet.view.AbstractView#render
/**
* 根据指定的模型准备视图,如有必要,将其与静态属性和RequestContext属性合并。
* 委托renderMergedOutputModel进行实际的渲染。
* @see #renderMergedOutputModel
*/
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response