Java程序员几乎都逃不过没日没夜地写rest api,改rest api,最后跑路[旺柴]的宿命。Java web发展到如今,那必然少不了一个得心应手的框架,好的框架使开发如鱼得水,事半功倍,妙啊[坏笑]。漏洞百出的Strurt曾经风靡一时的日子一去不复返,取而代之的是小巧轻便的spring MVC占据了大壁江山,期待下一次的技术变革[机智],让新框架变得可以自动生成业务代码,成熟的框架要会自己动[旺柴]。作为如今哪里需要哪里搬的码农,一起来搞懂spring MVC启动加载、工作流程机制吧。giegie我们一起学习啊,天呐,giegie跟我一起学习,你女朋友知道了,不会生气吧,你女朋友好可怕,不像我,我只想跟giegie学习。
将一个web应用程序部署到web容器中时,在web应用程序开始处理客户端请求之前,必须按此顺序执行以下步骤。
一、实例化部署描述符中由<listener>元素标识的每个事件监听器的实例。对于实现ServletContextListener的实例化监听器实例,会调用contextInitialized()方法。
二、实例化部署描述符中由<filter>元素标识的每个过滤器的实例,并调用每个过滤器实例的init()方法。
三、实例化由<servlet>元素标识的每个servlet的实例,该元素按照load on startup元素值定义的顺序包含<load on startup>元素,并调用每个servlet实例的init()方法。
从Tomcat源码中可看出,监听器启动成功,则启动过滤器,过滤器启动成功,则启动servlet,若某一个环节出错,则不进行后续的加载。
@Override
protected synchronized void startInternal() throws LifecycleException {
// Configure and call application event listeners
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
}
一、通过给定的servlet context初始化spring web应用上下文。
搭建一个springMVC项目的时候,会在web.xml里指定listener-class,通常使用的是org.springframework.web.context.ContextLoaderListener,该监听器可以说是web容器跟web项目的切入点,或者说连接点,当web容器启动过程中会实例化该监听器,启动好之后会改调用监听器的contextInitialized方法来初始化root web应用上下文。经常使用的Tomcat容器会在org.apache.catalina.core.StandardContext的listenerStart方法中发布事件org.apache.catalina.core.ApplicationContextFacade,就会通知到ContextLoaderListener监听器执行web应用上下文的初始化。说白的就是Tomcat启动好了,接下来就会把web应用部署到Tomcat中,这个过程中Tomcat会加载ContextLoaderListener实例对象,后面在org.apache.catalina.core.StandardContext的listenerStart方法执行的时候会调用ContextLoaderListener实例对象的contextInitialized方法来加载web应用上下文。
package org.apache.catalina.core;
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
public boolean listenerStart() {
// 省略
for (Object instance : instances) {
if (!(instance instanceof ServletContextListener)) {
continue;
}
ServletContextListener listener = (ServletContextListener) instance;
try {
fireContainerEvent("beforeContextInitialized", listener);
if (noPluggabilityListeners.contains(listener)) {
listener.contextInitialized(tldEvent);
} else {
listener.contextInitialized(event);
}
fireContainerEvent("afterContextInitialized", listener);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
fireContainerEvent("afterContextInitialized", listener);
getLogger().error(sm.getString("standardContext.listenerStart",
instance.getClass().getName()), t);
ok = false;
}
}
return ok;
}
}
org.springframework.web.context.ContextLoaderListener实现了servlet的中的接口,既实现了javax.servlet.ServletContextListener接口,该接口中定义了初始化和销毁web应用上下文的方法,同时ContextLoaderListener的各种实际的功能实现都是在父类org.springframework.web.context.ContextLoader中实现,并没有进行重写,当ContextLoaderListener接收的相应事件的时候直接调用父类中实现的方法,从而实现了web容器跟spring MVC通过ServletListener实现了耦合。
web容器调用ContextLoaderListener的contextInitialized方法,在该方法中直接调用了在ContextLoader父类中实现的initWebApplicationContext(ServletContext servletContext)方法来进行web应用上下文的初始化。整个过程包括创建一个ConfigurableWebApplicationContext类型的上下文对象content,接着对 该对象 进行configureAndRefreshWebApplicationContext 配置和刷新。
package org.springframework.web.context;
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
}
package org.springframework.web.context;
public class ContextLoader {
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//省略
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
//省略
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
// 省略
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
return this.context;
//省略
}
}
1、配置
1.1、设置上下文的id
默认是org.springframework.web.context.WebApplicationContext:配置的访问路径
1.2、把servlet context作为设置到spring web上下文的属性并设置进去,为spring web上下文设置配置路径(/WEB-INF/applicationContext.xml)会在该配置文件中指定扫描路径,配置bean对象等等。等会加载web 应用上下文的时候会根据该文件中的配置去解析加载。其实就像ClassPathXmlApplicationContext中一样,只不过是直接指定配置文件位置的,而这里的话是在web.xml的init-param标签中设置的。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.qiaoHaoTing.*"/>
</beans>
1.3、进行一些自定义
1.4、加载环境配置
2、刷新
刷新动作是在抽象应用上下文AbstractApplicationContext中定义,没有对刷新动作进行重写的spring上下文都是直接使用AbstractApplicationContext中定义的。这里刷新就不过多进行描述,可以参考上一篇博文:https://blog.youkuaiyun.com/qq_34485626/article/details/116088267,有区别的地方可能就是会去扫描我们定义的controller接口实例(如果定义了的话,并且扫描路径指定到了controller包)。完成加载之后可以看到定义service、controller、bean后置处理器等对象都已被实例化。
二、实例化给定的过滤器并调用其init()方法
监听器加载完毕之后,若配有过滤器,则接下来会把配置好的过滤器依次实例化以及执行init方法,所以说过滤器并非必须要配置的。来看一下servlet包下的过滤器顶级接口Filter,定义了三个方法。
init方法是会被web容器调用,被调用后,过滤器才能在整个服务中正常使用。该方法只应被调用一次,并且在过滤器开始工作前;
doFilter该方法定义了过滤器实际的工作逻辑,每个请求或者响应来到链路中的时候,web容器就会调用该方法对本次的请求或者响应进行一些检查、包装工作;
destroy方法将在web容器要停止服务(关闭)时会被调用,并且会等待过滤器的doFilter方法中所有工作处理完成或者等待执行超时,该方法才会被执行,执行最终的销毁工作,能够释放掉所持有的资源,比如内存,文件句柄,线程资源等等,确保任何持久状态都与内存中过滤器的当前状态同步。
实现了该接口的类便可称为过滤器,负责对资源的请求或者响应执行过滤任务,每个过滤器对象中会持有一个FilterConfig对象,通过其FilterConfig对象,获取其初始化参数,以及前一步加载好的servletcontext。我们在web.xml中配置好的初始化参数init-param,web容器会去执行解析,放入FilterConfig对象,在初始化init的时候作为参数传过来。
package javax.servlet;
import java.io.IOException;
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;
public void destroy();
}
以Tomcat容器为例,如果监听器加载成功,则接下来就进行过滤器的加载。启动的时候会解析web.xml中的配置定义,放入上下文context名为filterDefs的map集合中,在初始化过滤器配置的时候遍历集合,一一进行实例化并执行init()方法。每个过滤器会对应一个把过滤器配置对象,让context持有应用过滤器配置对象,过滤器配置对象中维护着过滤器信息,在过滤器对象实例化的时候会实例化过滤器
public boolean filterStart() {
// 省略
synchronized (filterConfigs) {
filterConfigs.clear();
for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
String name = entry.getKey();
if (getLogger().isDebugEnabled()) {
getLogger().debug(" Starting filter '" + name + "'");
}
try {
ApplicationFilterConfig filterConfig =
new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
// 省略
}
}
}
}
ApplicationFilterConfig(Context context, FilterDef filterDef)
throws ClassCastException, ReflectiveOperationException, ServletException,
NamingException, IllegalArgumentException, SecurityException {
super();
this.context = context;
this.filterDef = filterDef;
// Allocate a new filter instance if necessary
if (filterDef.getFilter() == null) {
getFilter();
} else {
this.filter = filterDef.getFilter();
context.getInstanceManager().newInstance(filter);
initFilter();
}
}
Filter getFilter() throws ClassCastException, ReflectiveOperationException, ServletException,
NamingException, IllegalArgumentException, SecurityException {
// Return the existing filter instance, if any
if (this.filter != null)
return this.filter;
// Identify the class loader we will be using
String filterClass = filterDef.getFilterClass();
this.filter = (Filter) context.getInstanceManager().newInstance(filterClass);
initFilter();
return this.filter;
}
private void initFilter() throws ServletException {
if (context instanceof StandardContext &&
context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
filter.init(this);
} finally {
String capturedlog = SystemLogHandler.stopCapture();
if (capturedlog != null && capturedlog.length() > 0) {
getServletContext().log(capturedlog);
}
}
} else {
filter.init(this);
}
// Expose filter via JMX
registerJMX();
}
过滤器以org.springframework.web.filter.CharacterEncodingFilter为例,该类维护了两个成员变量,一个是编码encoding,一个是 是否强制编码forceEncoding;具体的doFilter方法在父类中实现,doFilter中调用的doFilterInternal由子类自己进行实现,CharacterEncodingFilter则重写了父类的doFilterInternal方法,在该doFilterInternal方法中,如果设置了编码encoding,同时如果强制进行编码/请求中没有设置编码,则将请求的编码设置为encoding;如果强制编码,则将响应的编码设置为encoding。初始化init方法也是在父类中实现,在GenericFilterBean中实现了init方法,但只负责通过包装器把初始化参数设置到过滤器对象属性中,接着调用了一个由其子类实现的方法initFilterBean,交给子类去做具体的初始化动作。
在web.xml中把CharacterEncodingFilter配置进去,encoding设置为UTF-8,过滤路径设置为过滤所有请求。
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
通过下面的debug截图可以看出,init方法执行完毕之后web.xml中设置的初始化参数被赋值给CharacterEncodingFilter过滤器实例化对象filter,CharacterEncodingFilter并没有对initFilterBean方法进行重写,没有做进一步的初始化动作。
三、加载servlet
先来看一下DispatcherServlet类定义和结构(如下UML类图),DispatcherServlet被官方定义为HTTP请求处理程序/控制器的中央调度器,例如用于web UI控制器或基于HTTP的远程服务导出器。分派给已注册的处理程序以处理web请求,从而提供方便的映射和异常处理工具。
DispatcherServlet可以使用任何处理器映射器HandlerMapping实现(预构建或作为应用程序的一部分提供)来控制请求到处理程序对象的路由。spring MVC中默认的处理映射器实现有这两个:org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping和org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping。当然也可以自己定义处理映射器HandlerMapping,实现HandlerMapping接口,把自定义对象在servlet的应用程序上下文中定义为bean,覆盖默认HandlerMapping即可(如果存在)。HandlerMappings可以被赋予任何bean名称(它们是按类型测试的)。
DispatcherServlet可以使用任何处理适配器handleAdapter,允许使用任何处理程序接口。默认提供了org.springframework.web.servlet.mvc.httprequesthandleadapter和org.springframework.web.servlet.mvc.simplecontrollerhandleadapter两个适配器,分别对应于Spring的org.springframework.web.HttpRequestHandler和org.springframework.web.servlet.mvc.Controller接口。默认的org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter也将被注册。handleAdapter对象可以作为bean添加到应用程序上下文中,覆盖默认HandlerAdapters。与HandlerMappings一样,HandlerAdapter可以被赋予任何bean名称(它们是按类型测试的)。
调度器的异常解决策略可以通过HandlerExceptionResolver指定,例如将某些异常映射到错误页的示例。默认提供了
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver、org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver以及 org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver三个异常解析器,HandlerExceptionResolver异常处理解析器也可以通过自动义重写到spring上下文中。HandlerExceptionResolver也可以被赋予任何bean名称(它们是按类型测试的)。
视图解析策略可以指定实现了ViewResolver的类对象,即解析符号视图将名称添加到视图对象中。默认的视图解析器有org.springframework.web.servlet.view.InternalResourceViewResolver。ViewResolver对象可以作为bean添加到应用程序上下文中,从而覆盖默认的ViewResolver。ViewResolver可以被赋予任何bean名称(它们是按类型测试的)。
web容器在监听器和过滤器加载完毕之后,会进行servlet的加载,即,实例化servlet对象和调用该对象的init方法。通常在web.xml配置中指定servlet-name名称,指定servlet-class具体实现类,该类一般为org.springframework.web.servlet.DispatcherServlet,指定init-param初始化参数以及请求路径映射等等。以Tomcat容器为例,Tomcat完成该动作是在类StandardWrapper的loadServlet方法中实现。通过newInstance和类名实例化servlet对象,接着调用servlet的init初始化方法来完成servlet的加载。实例化DispatcherServlet对象,然后调用带对象的init方法。
package org.apache.catalina.core;
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
/**
* Load and initialize an instance of this servlet, if there is not already
* at least one initialized instance. This can be used, for example, to
* load servlets that are marked in the deployment descriptor to be loaded
* at server startup time.
* @return the loaded Servlet instance
* @throws ServletException for a Servlet load error
*/
public synchronized Servlet loadServlet() throws ServletException {
//省略
Servlet servlet;
//省略
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
servlet = (Servlet) instanceManager.newInstance(servletClass);
//省略
initServlet(servlet);
//省略
return servlet;
}
private synchronized void initServlet(Servlet servlet)
throws ServletException {
if (instanceInitialized && !singleThreadModel) return;
if( Globals.IS_SECURITY_ENABLED) {
// 省略
} else {
servlet.init(facade);
}
// 省略
}
}
通过UML类图可知,DispatcherServlet的init()方法是在父类中实现的,顶层接口servlet定义了init(ServletConfig config)方法,抽象父类GenericServlet实现了该方法,在方法体里把容器传过来的config保留在了本地副本中,既赋值给了成语变量,然后再调用无参的init()方法,该无参的init()方法最终是在其子类HttpServletBean中具体实现。在HttpServletBean的init方法中,先把init param参数加载到bean 属性中,接下来的部分又交给了其子类FrameworkServlet的initServletBean()方法中来实现。在FrameworkServlet实现的initServletBean()中会初始化web应用上下文,注意,这里又进行了一次上下文的加载,前面加载的上下文作为这里的 parent。web应用上下文的加载主要是实例化的定义rest 接口的controller类对象、默认的映射处理器,处理适配器,异常解析器、一些注解类,如下图,完成单例对象实例化后集合中的成员对象。但是有木有发现这里没有实例化默认的视图解析器,莫慌莫慌,下面的策略初始化将会加载进来。
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected final void initServletBean() throws ServletException {
// 省略
// 初始化web应用上下文
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
// 省略
}
}
在加载web应用上下文过程中,实例化bean对象的时候,通过包装器把对象bean实例化出来之后,如果该类实现InitializingBean接口,并实现了afterPropertiesSet方法,则会调用该对象bean的afterPropertiesSet方法。
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
// 省略
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// 省略
((InitializingBean) bean).afterPropertiesSet();
}
// 省略
}
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping则实现了InitializingBean接口,并在afterPropertiesSet方法中实现了 把controller类中的 rest接口 转化为RequestMappingInfo和HandlerMethod,然后存储在该对象bean的urlMap和handlerMethods两个Map集合中。一个rest接口方法对应着一个HandlerMethod和一个RequestMappingInfo;urlMap中以请求路径为key,以RequestMappingInfo作为value;handlerMethods以RequestMappingInfo作为key,以HandlerMethod作为value;
rest接口的每个方法定义对应着一个HandlerMethod对象,rest接口的RequestMapping注解对应着一个RequestMappingInfo对象,对应关系如下。
package org.springframework.web.method;
public class HandlerMethod {
// rest接口类的类名
private final Object bean;
// dispatcher servlet对应的上下文
private final BeanFactory beanFactory;
// rest接口方法定义
private final Method method;
// 桥接方法,如果不存在则为方法本身method
private final Method bridgedMethod;
// rest接口方法参数
private final MethodParameter[] parameters;
}
如下debug截图,在RequestMappingHandlerMapping对象的执行完afterPropertiesSet方法后,完成了对rest接口的解析。
@Controller
@RequestMapping(value = "/user")
public class UserController {
@Autowired
UserService userService;
@RequestMapping(value = "/info", method = RequestMethod.GET)
@ResponseBody
public Response userInfo(@RequestParam Integer id) {
return Response.success(userService.getUserInfo(id));
}
}
应用上下文加载的最后广播器会把 上下文加载完成的事件 发布出去,在上面的加载过程中,会给广播器注入一个默认的ContextRefreshListener监听对象,该监听对象收到上下文加载完成事件后执行initStrategies的方法来初始化此DispatcherServlet使用的策略对象。
initMultipartResolver(context)——初始化文件上传解析器,如果配置了则初始化,否则不处理;
initLocaleResolver(context)——初始化国际化解析器,如果有定义配置则进行初始化,否则不处理;
initThemeResolver(context)——初始化主题解析器,如果有定义配置则进行初始化,否则不处理;
initHandlerMappings(context)——初始化处理映射器,在加载上面的web 应用上下文的时候已经做了初始化,这里做的是保存一份副本。
initHandlerAdapters(context)——初始化处理适配器,在加载上面的web 应用上下文的时候已经做了初始化,这里做的是保存一份副本。
initHandlerExceptionResolvers(context)——初始化异常解析器,在加载上面的web 应用上下文的时候已经做了初始化,这里做的是保存一份副本。
initRequestToViewNameTranslator(context)——初始化请求转视图转化器,如果有定义配置则进行初始化,否则不处理;
initViewResolvers(context)——初始化视图解析器,刚才进行web 应用上下文加载的时候并没有初始化该对象,这里会加载默认的org.springframework.web.servlet.view.InternalResourceView。
initFlashMapManager(context)——初始化FlashMapManager(重定向前后信息存储),如果有定义配置则进行初始化,否则不处理;
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
这里策略处理器都有着对应的实现类,需要使用的话,配上去即可,这里不做过多的描述。比如常用的文件上传,在dispatcher-servlet.xml作如下配置即可。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>9242880</value>
</property>
</bean>
DispatcherServlet初始化好之后会加载好如下图所示的默认属性
四、请求处理
4.1、执行过滤器
当一个请求过来的时候,如果设置了过滤器,则先执行过滤,web容器会调用过滤器的doFilter方法。
package org.apache.catalina.core;
public final class ApplicationFilterChain implements FilterChain {
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
// 省略
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
}
// We fell off the end of the chain -- call the servlet instance
// 省略
servlet.service(request, response);
// 省略
}
}
会逐一执行过滤链ApplicationFilterChain中的过滤器,不同的过滤器在自己的doFilterInternal方法中实现自己具体的过滤逻辑,过滤器CharacterEncodingFilter则把请求/响应中的编码设置成指定的编码。在所有的过滤器执行完毕,请求通过了过滤器的过滤逻辑(比如做了权限校验,提前返回),接下来则会调用servlet。
4.2、servlet接收到标准的http请求,根据请求方法(GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE)调用对应的处理函数。在每种请求处理函数中会进行一些特殊处理外,最终调用processRequest方法来处理请求,并在处理完之后,会将请求、耗时、异常信息作为一个事件发布出去。processRequest中请求处理调用doService方法去完成,在doService中进行一些副本快照保存以及请求属性设置后,后续逻辑在doDispatch中实现。转发,为什么
4.3、doDispatch,调度,根据请求的url/名称找到对应的接口,或者说接口对应的handlerMethod(应用启动的时候,会将接口转化为handlerMethod存放在web应用上下文中),接着找到合适的适配器HandlerAdapter并执行接口方法,返回ModelAndView,最后使用ViewResolver对ModelAndView进行解析,把逻辑视图解析为真正的视图View对象。注意,如果是纯接口,适配器执行返回的ModelAndView结果为null,就相当于没有视图,在适配器执行最后会将接口的执行结果写入到请求响应中。如今,web应用应该都是前后端分离的,后端只负责处理数据,不再进行视图渲染。
package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
a、为当前请求查找处理器handler。在servlet初始化的时候已经加载好了handlerMapping,当一个请求过来的时候会遍历加载好的handlerMapping,在handlerMapping中找到一个对应的HandlerMethod,主要根据请求的url去匹配handlerMapping的handlerMethod,匹配上的话,会new一个新的对应handlerMethod,最后并进一步封装成一个HandlerExecutionChain(会加入handlerMapping的拦截器)。
package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
}
最终生成的HandlerExecutionChain会包含匹配到的handlerMethod以及interceptor。
b、为当前请求查找处理适配器handler Adapter。跟handlerMapping一样,handler Adapter在servlet初始化的时候已经加载好了。遍历适配器,找到提前返回,判定当前适配器是否适用于当前处理器,RequestMappingHandlerAdapter的判定就较为简单,处理器是否实现了HandlerMethod接口。
package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
}
c、执行拦截器的preHandle方法。HandlerInterceptor总共定义了preHandle、postHandle以及afterCompletion三个方法,每个方法有着不同的执行时机。preHandle在确定了处理器和处理器适配器之后执行,postHandle在适配器执行invoke后执行,afterCompletion在请求处理完成后回调,即渲染视图后回调,可以允许适当的进行清理回收资源。
d、执行实际的处理方法。将通过反射执行handlerMethod对应的rest接口的方法,然后将执行返回结果写入到web请求,最后返回ModelAndView。
e、执行拦截器的postHandle方法。
f、如果返回的ModelAndView不为空,则进行视图渲染。根据ModelAndView得到view,然后调用view的render方法进行渲染。第一步是准备请求,比如在JSP中,这意味着将模型对象设置为请求属性;第二步是视图的实际呈现,例如通过RequestDispatcher包含JSP。
g、执行拦截器的afterCompletion方法。