阅读完需:约 81 分钟
在学习后面的webFlux时,还需要回过来了解一下MVC,不然会有很多的疑惑,因为MVC中也是存在着类似于响应式的方式。
既然要从头看看MVC,那么就看最原始的地方开始,了解一下MVC的整个执行过程。
请求处理流程
在SpringMVC之前,Java Web应用开发使用Servlet规定的工作模式如下:
- 客户端发送请求至服务器
- 服务器启动并调用Servlet
- Servlet根据客户端请求生成相应内容并传给服务器
- 服务器响应返回给客户端
以上是一个高度抽象的请求处理流程,但其中重要的工作原理依然适用。 当它被部署在应用服务器(Tomcat、Jetty等等)中,会由Servlet 容器控制Servlet的生命周期(容器就是服务器中用于管理Java组件的部分)。
除非特殊指定,Servlet只会在第一次请求的时候被加载和实例化,这也是为什么大多数情况第一次访问web服务会比较慢的原因。 一旦Servlet被加载,一般不会从容器中删除(除非容器执行了存储器回收动作),直到应用服务器关闭或重新启动。
一个请求达到Spring MVC,它的一个处理流程。

- 用户向服务器发送请求,请求被Spring前端控制器
DispatcherServlet捕获 -
DispatcherServlet对请求URL进行解析,然后通过HandlerMapping的比对找到对应的Handler -
HandlerAdapter负责真正调用控制器方法(注意:执行之前需要先请求执行链中的拦截器的preHandle方法进行拦截,返回true就继续执行,返回false就不继续执行了) - 控制器执行完成,向
DispatcherServlet返回一个ModelAndView对象,然后就执行postHandle方法 - 根据返回的
ModelAndView,选择一个适合的ViewResolver视图解析器(必须是已经注册到Spring容器中的ViewResolver) -
ViewResolver结合Model和View进行视图的渲染 - 将渲染的结果写入
Response返回给客户端
细节的处理步骤

执行流程的源码分析
测试的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
在SpringMVC中这个类及其关键,所以我们对MVC的理解取决于对DispatcherServlet的理解。
先来看看DispatcherServlet的继承关系图

在这关系图中最关键的几个类分别是DispatcherServlet,FrameworkServlet,HttpServletBean,GenericServlet,还有一个Servlet接口,Wrapper接口等
首先我们看看我们第一次调用接口时,项目是如何初始化的,这会牵扯到很多的类,核心还是DispatcherServlet,我们就已它作为展开的起点。
StandardWrapperValve——invoke
org.apache.catalina.core.StandardWrapperValve#invoke
StandardWrapperValue是Wrapper的标准阀,用在Pipleline流程中的最后一个valve执行。
pipeline就是与容器关联的一系列处理流程,StandardWrapperValve在初始化的时候就被加入到了pipeline中

这里先不讲Tomcat的启动流程,当请求来到Tomcat的最后一个valve,StandardWrapperValve时。执行其invoke方法。


源码中的invoke方法非常的长,包含了异步和同步的处理,这里就只写最关键的内容
final class StandardWrapperValve
extends ValveBase {
// 调用我们正在管理的 servlet,遵守有关 servlet 生命周期和 SingleThreadModel 支持的规则。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 1.Allocate a servlet instance to process this request
//分配一个servet 去处理请求
servlet = wrapper.allocate();
// 2.Create the filter chain for this request
//封装servlet, 为当前请求创建一个过滤器链
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
//3.执行过滤器链的doFilter方法,(链式执行各种Filter ,末端执行servlet's service() method)
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
}
在这里最关键的就是servlet = wrapper.allocate();方法调用,它会去请求这个类StandardWrapper,来进行一个初始化流程。
当初始化完成后就会进行过滤器链的调用,关于调用后面再讲
当执行到servlet = wrapper.allocate();就会进入StandardWrapper类分配一个servet
StandardWrapper——allocate
org.apache.catalina.core.StandardWrapper#allocate
在这个类里做一些初始化的操作,这个类实现了一个接口Wrapper



Wrapper容器是最小的容器,每个Wrapper都可以对应一个Servlet的实例。当请求转发到Wrapper容器之后,wrapper容器在调用Pipeline方法之后,会使用特定的类加载器去加载servlet类(实现servlet接口的类,比如HttpServletBean),对servlet进行实例化和初始化,然后将请求交给servelt的service(比如实现了servelt接口的HttpServlet)方法进行处理。
我们常见的Spring的DispatcherServlet是线程安全的,所以Tomcat不需要保证Servlet的并发安全。对于非线程安全的servlet,则可以通过SingleThreadModel来保证多请求下servlet的正常运行。
Wrapper容器的主要作用就是载入servlet类并进行实例化,并调用service方法。当第一次请求某个servlet类的时候,Wrapper容器会载入servlet类。Tomcat提供了专门的类加载器用于加载servlet。
Wrapper容器的基本阀门StandardWrapperValve还会在调用servelt容器之前调用用户配置的过滤器链Filter。

allocate 方法 — 实例化Servlet
/**
* 分配此 Servlet 的已初始化实例,该实例已准备好调用其service()方法。
* 如果 servlet 类没有实现SingleThreadModel ,则(唯一的)初始化实例可能会立即返回。
* 如果 servlet 类实现SingleThreadModel ,则 Wrapper 实现必须确保在调用deallocate()将其释放之前不会再次分配此实例。
* @return
* @throws ServletException
*/
public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception
if (unloading) {
throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
}
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled()) {
log.debug("Allocating non-STM instance");
}
// Note: We don't know if the Servlet implements
// SingleThreadModel until we have loaded it.
//加载Servlet
instance = loadServlet();
newInstance = true;
if (!singleThreadModel) {
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case
// #3
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
}
if (!instanceInitialized) {
// 第一次请求接口初始化Servlet
initServlet(instance);
}
}
}
if (singleThreadModel) {
if (newInstance) {
// Have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (instancePool) {
instancePool.push(instance);
nInstances++;
}
}
} else {
if (log.isTraceEnabled()) {
log.trace(" Returning non-STM instance");
}
// 如果不是 SingleThreadModel 只会存在一个实例,所以直接返回唯一的实例就行
// SingleThreadModel 表示同一个对象在同一时刻只能被一个线程访问,所以会创建多个对象来处理多线程。
// 这里会用对象池来管理对象
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
}
return instance;
}
}
synchronized (instancePool) {
// 活动数量大于实例数量,要一直创建新的 Servlet 对象
// 注意这个活动数量不是 Servlet 的数量,应该是活动线程的数量
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
// 如果实例数量小于最大实例数量,创建新的 Servlet 对象
// 注意这里最大数量默认 20
try {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
} else {
// 如果实例数量达到最大数量,等待其它 Servlet 结束
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
if (log.isTraceEnabled()) {
log.trace(" Returning allocated STM instance");
}
// 如果活动线程数量小于实例数量,表示有空闲的 Servlet 对象来处理活动线程
// 这里直接返回空闲的 Servlet 对象
countAllocated.incrementAndGet();
return instancePool.pop();
}
}
StandardWrapper——initServlet
org.apache.catalina.core.StandardWrapper#initServlet
在allocate方法中被调用,初始化容器
private synchronized void initServlet(Servlet servlet)
throws ServletException {
// 判断是否是实例初始化并且判断这个 servlet 是否实现了 SingleThreadModel 接口,反正就是已经初始化了,直接返回
if (instanceInitialized && !singleThreadModel) return;
// Call the initialization method of this servlet
try {
if( Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
Object[] args = new Object[] { facade };
SecurityUtil.doAsPrivilege("init",
servlet,
classType,
args);
success = true;
} finally {
if (!success) {
// destroy() will not be called, thus clear the reference now
SecurityUtil.remove(servlet);
}
}
} else {
// 调用servlet接口的init方法,初始化servlet,这里看HttpServletBean的init方法
servlet.init(facade);
}
// 设置为已经初始化状态
instanceInitialized = true;
} catch (UnavailableException f) {
unavailable(f);
throw f;
} catch (ServletException f) {
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw f;
} catch (Throwable f) {
ExceptionUtils.handleThrowable(f);
getServletContext().log(sm.getString("standardWrapper.initException", getName()), f);
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw new ServletException
(sm.getString("standardWrapper.initException", getName()), f);
}
}
initServlet调用了Servlet的init方法
/**
由 servlet 容器调用以向 servlet 指示 servlet 正在投入使用。
servlet 容器在实例化 servlet 后恰好调用一次init方法。 init方法必须成功完成,servlet 才能接收任何请求。
如果init方法,则 servlet 容器无法将 servlet 投入服务
抛出ServletException
在 Web 服务器定义的时间段内不返回
参数:
config – 一个ServletConfig对象,包含 servlet 的配置和初始化参数
抛出:
ServletException – 如果发生了干扰 servlet 正常运行的异常
也可以看看:
UnavailableException , getServletConfig
*/
public void init(ServletConfig config) throws ServletException;
HttpServletBean——init
org.springframework.web.servlet.HttpServletBean#init
HttpServletBean 继承自 HttpServlet,它负责将 init-param 中的参数注入到当前 Servlet 实例的属性中,同时也为子类提供了增加 requiredProperties 的能力,需要注意的是 HttpServletBean 并不依赖于 Spring 容器。


大家知道,HttpServlet 的初始化是从 init 方法开始的,我们从HttpServletBean 的 init 方法开始看起:
/**
* 将配置参数映射到此 servlet 的 bean 属性,并调用子类初始化。
* 抛出:
* ServletException – 如果 bean 属性无效(或缺少必需的属性),或者子类初始化失败。
* @throws ServletException
*/
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new HttpServletBean.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;
}
}
// Let subclasses do whatever initialization they like.
// 看这里,子类实现,这里的子类是FrameworkServlet,注意FrameworkServlet是DispatcherServlet的父类
initServletBean();
}
在这个方法里,首先获取到 Servlet 的所有配置并转为 PropertyValues,然后通过 BeanWrapper 修改目标 Servlet 的相关属性。
BeanWrapper 是 Spring 中提供一个工具,使用它可以修改一个对象的属性。
例如
public static void main(String[] args) {
User user = new User();
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(user);
beanWrapper.setPropertyValue("name", "hello");
PropertyValue pv = new PropertyValue("id", 18L);
beanWrapper.setPropertyValue(pv);
System.out.println("user = " + user.getName()+","+user.getId());
}
// 结果 user = hello,18
所以前面的 bw 实际上就代表当前 DispatcherServlet 对象。
通过 BeanWrapper 修改目标 Servlet 的相关属性时,有一个 initBeanWrapper 方法是空方法,开发者如有需要可以在子类中实现该方法,并且完成一些初始化操作。
FrameworkServlet——initServletBean
org.springframework.web.servlet.FrameworkServlet#initServletBean



initServletBean()是HttpServletBean类的抽象方法在FrameworkServlet类中去实现了它
/**
* HttpServletBean的重写方法,在设置任何 bean 属性后调用。创建此 servlet 的 WebApplicationContext。
* @throws ServletException
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
// 看这里,初始化操作在这实现
this.webApplicationContext = initWebApplicationContext();
// 空实现,留给子类实现
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
这个方法关键在于设置属性与创建WebApplicationContext
-
initWebApplicationContext方法用来初始化WebApplicationContext。 -
initFrameworkServlet方法用来初始化FrameworkServlet,但是这个方法是一个空方法,没有具体的实现。本来子类可以重写该方法做一些初始化操作,但是实际上子类并没有重写该方法,所以这个方法我们就暂且忽略之,不去分析了。
FrameworkServlet——initWebApplicationContext
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
/**
* 初始化并发布此 servlet 的 WebApplicationContext。
* 委托createWebApplicationContext实际创建上下文。可以在子类中覆盖。
* 回报:
* WebApplicationContext 实例
* 也可以看看:
* FrameworkServlet(WebApplicationContext) , setContextClass , setContextConfigLocation
* @return
*/
protected WebApplicationContext initWebApplicationContext() {
// 获取WebApplicationContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
// 如果当前Spring的上下文对象没有被激活即没有调用refresh方法,在这里会调用refresh方法
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
// 这里会调用DispatcherServlet的onRefresh方法
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
分析一下这里的逻辑
- 首先获取
rootContext。在默认情况下,Spring 会将容器设置为ServletContext的一个属性,属性的 key 为org.springframework.web.context.WebApplicationContext.ROOT,所以根据这个 key 就可以调用ServletContext#getAttribute方法获取到rootContext了。 - 获取
WebApplicationContext实例,也就是给 wac 变量赋值的过程,这里存在三种可能性:- 如果已经通过构造方法给
webApplicationContext赋值了,则直接将其赋给 wac 变量,同时,如果需要设置 parent 就设置,需要刷新就刷新。这种方式适用于 Servlet3.0 以后的环境,因为从 Servlet3.0 开始,才支持直接调用ServletContext.addServlet方法去注册 Servlet,手动注册的时候就可以使用自己提前准备好的WebApplicationContext了 - 如果第一步没能成功给 wac 赋值,那么调用
findWebApplicationContext方法尝试去ServletContext中查找WebApplicationContext对象,找到了就赋值给 wac; - 如果第二步没能成功给 wac 赋值,那么调用
createWebApplicationContext方法创建一个WebApplicationContext对象并赋值给 wac,一般来说都是通过这种方式创建的WebApplicationContext。这三套组合拳下来,wac 肯定是有值了。
- 如果已经通过构造方法给
- 当
ContextRefreshedEvent事件没有触发时,调用onRefresh方法完成容器刷新(由于第一种和第三种获取WebApplicationContext的方式最终都会调用configureAndRefreshWebApplicationContext方法,然后发布事件,再将refreshEventReceived变量标记为 true,所以实际上只有第二种方式获取 wac 实例的时候,这里才会刷新,具体可以看下文分析)。 - 最后将 wac 保存到到
ServletContext中。保存的时候会根据publishContext变量的值来决定是否保存,publishContext 可以在 web.xml 中配置 Servlet 时通过 init-param 进行配置,保存的目的是为了方便获取。
如果没有WebApplicationContext就会去创建它,这里我们也可以来看看
FrameworkServlet——createWebApplicationContext——configureAndRefreshWebApplicationContext
org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext#configureAndRefreshWebApplicationContext
/**
* 实例化此 servlet 的 WebApplicationContext,可以是默认XmlWebApplicationContext或custom context class (如果已设置)。代表#createWebApplicationContext(ApplicationContext)。
* 参数:
* parent – 要使用的父 WebApplicationContext,如果没有则为null
* 回报:
* 此 servlet 的 WebApplicationContext
* 也可以看看:
* XmlWebApplicationContext , createWebApplicationContext(ApplicationContext)
*/
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
/**
* 实例化此 servlet 的 WebApplicationContext,可以是默认XmlWebApplicationContext或custom context class (如果已设置)。
* 此实现需要自定义上下文来实现ConfigurableWebApplicationContext接口。可以在子类中覆盖。
* 不要忘记在创建的上下文中将此 servlet 实例注册为应用程序侦听器(用于触发其callback ,并在返回上下文实例之前调用ConfigurableApplicationContext.refresh() 。
* 参数:
* parent -- 要使用的父 ApplicationContext,如果没有,则为null
* 回报:
* 此 servlet 的 WebApplicationContext
* 也可以看看:
* XmlWebApplicationContext
*/
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
这里这几个方法是连在一起的
createWebApplicationContext
首先获取到创建类型,并检查创建类型,没问题的话调用 instantiateClass 方法完成创建工作,然后给创建好的 wac 对象配置各种属性,配置的 configLocation 就是我们在 web.xml 文件中配置的 SpringMVC 配置文件路径,默认的文件路径是 /WEB-INF/[servletName]-servlet.xml。
configureAndRefreshWebApplicationContext
configureAndRefreshWebApplicationContext 方法主要也是配置&刷新 WebApplicationContext,在这个方法里会调用 addApplicationListener 为 wac 添加一个监听器,监听的是 ContextRefreshedEvent 事件,当收到该事件后,会调用 FrameworkServlet 的 onApplicationEvent 方法,并在该方法中调用 onRefresh 方法完成刷新,刷新之后,会将 refreshEventReceived 变量标记为 true。
/**
从该 servlet 的 WebApplicationContext 接收刷新事件的回调。
默认实现调用onRefresh ,触发刷新此 servlet 的上下文相关状态。
参数:
event – 传入的 ApplicationContext 事件
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
这就是 FrameworkServlet#initServletBean 方法的大致工作逻辑。这里涉及到了 onRefresh 方法,兜兜转转也是回到了onRefresh方法,但是这是一个空方法,在子类 DispatcherServlet 中实现了,所以接下来我们就来看 DispatcherServlet。
DispatcherServlet——onRefresh——initStrategies
org.springframework.web.servlet.DispatcherServlet#onRefresh#initStrategies
终于执行到DispatcherServlet这个类了,这个类里的内容也是重点

@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* 这个方法可以看到调用了各种初始化方法
* 初始化此 servlet 使用的策略对象。
* 可以在子类中被覆盖以初始化更多的策略对象。
*/
protected void initStrategies(ApplicationContext context) {
//初始化上传文件
initMultipartResolver(context);
//初始化local
initLocaleResolver(context);
//初始化主题
initThemeResolver(context);
//初始化映射器
initHandlerMappings(context);
//初始化adapter
initHandlerAdapters(context);
//初始化异常处理器
initHandlerExceptionResolvers(context);
//初始化视图名称转换器
initRequestToViewNameTranslator(context);
//初始化视图处理器
initViewResolvers(context);
//初始化 Flash 地图管理器
initFlashMapManager(context);
}
initMultipartResolver
/**
初始化此类使用的 MultipartResolver。
如果在此命名空间的 BeanFactory 中没有使用给定名称定义 bean,则不提供多部分处理。
*/
private void initMultipartResolver(ApplicationContext context) {
try {
//public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
//这里只是给multipartResolver赋值,对象是从ioc容器中拿出来的。注意这里的beanName是multipartResolver,这里就能解释为什么配置MultipartResolver时方法名必须为multipartResolver
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
initLocaleResolver
/**
初始化此类使用的 LocaleResolver。
如果在此命名空间的 BeanFactory 中没有使用给定名称定义 bean,我们默认为 AcceptHeaderLocaleResolver。
*/
private void initLocaleResolver(ApplicationContext context) {
try {
//public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
//直接赋值,跟上面一样
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
initThemeResolver
/**
初始化此类使用的 ThemeResolver。
如果在此命名空间的 BeanFactory 中没有使用给定名称定义 bean,我们默认为 FixedThemeResolver。
*/
private void initThemeResolver(ApplicationContext context) {
try {
//public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
//初始化主题,跟上面一样
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.themeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
"': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
}
}
}
initHandlerMappings
/**
* 初始化此类使用的 HandlerMappings。
* 如果在 BeanFactory 中没有为此命名空间定义 HandlerMapping bean,我们默认为 BeanNameUrlHandlerMapping。
* @param context
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// detectAllHandlerMappings表示获取所有的mapping还是期望的mapping,默认为true,获取所有
if (this.detectAllHandlerMappings) {
// 获取所有mapping
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// 如果没有映射器,这里使用默认策略生成映射器
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
// 路径模式PathPatterns匹配,Spring5.3的新功能之前没有
for (HandlerMapping mapping : this.handlerMappings) {
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
initHandlerAdapters
/**
初始化此类使用的 HandlerAdapters。
如果没有在 BeanFactory 中为此命名空间定义 HandlerAdapter bean,我们默认为 SimpleControllerHandlerAdapter。
*/
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
//detectAllHandlerAdapters表示获取所有的adapter还是期望的adapter,默认为true,获取所有
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
//获取所有的adapter
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
//如果没有adapter,使用默认策略生成adapter
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
initHandlerExceptionResolvers
/**
初始化此类使用的 HandlerExceptionResolver。
如果在此命名空间的 BeanFactory 中没有使用给定名称定义 bean,我们默认为无异常解析器。
*/
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
//detectAllHandlerExceptionResolvers表示获取所有的异常处理器还是期望的异常处理器,默认为true,获取所有
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
//获取所有的异常处理器
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, no HandlerExceptionResolver is fine too.
}
}
// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found
//如果没有异常处理器,使用默认策略生成异常处理器
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
initRequestToViewNameTranslator
/**
初始化此 servlet 实例使用的 RequestToViewNameTranslator。
如果没有配置实现,那么我们默认为 DefaultRequestToViewNameTranslator。
*/
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
//public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
//获取视图名称转化器
this.viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.viewNameTranslator.getClass().getSimpleName());
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.viewNameTranslator);
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
if (logger.isTraceEnabled()) {
logger.trace("No RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
"': using default [" + this.viewNameTranslator.getClass().getSimpleName() + "]");
}
}
}
initViewResolvers
/**
初始化此类使用的 ViewResolvers。
如果没有在 BeanFactory 中为此命名空间定义 ViewResolver bean,我们默认为 InternalResourceViewResolver。
*/
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
//detectAllViewResolvers表示获取所有的视图处理器还是期望的视图处理器,默认为true,获取所有
if (this.detectAllViewResolvers) {
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
//获取所有的视图处理器
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<>(matchingBeans.values());
// We keep ViewResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
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.
}
}
// Ensure we have at least one ViewResolver, by registering
// a default ViewResolver if no other resolvers are found.
//没有视图处理器,使用默认策略生成视图处理器
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
九个的初始化流程比较类似,这里我们以常见的视图解析器的初始化方法 initViewResolvers 为例,来一起看看初始化流程:
一开始的 viewResolvers 变量是一个集合,解析出来的视图解析器对象都将放入这个集合中。
首先判断 detectAllViewResolvers 变量是否为 true,如果为 true,则直接去查找 Spring 容器中的所有视图解析器,将查找结果赋值给 viewResolvers,然后进行排序。默认情况下 detectAllViewResolvers 变量的值为 true,如果有需要,可以在 web.xml 中进行配置,像下面这样:
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<init-param>
<param-name>detectAllViewResolvers</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
如果 detectAllViewResolvers 的值为 false,那么接下来就会去 Spring 容器中查找一个名为 viewResolver 的视图解析器,此时查找到的就是一个单独的视图解析器。
一般来说,我们并不需要在 web.xml 中去配置 detectAllViewResolvers 的值,视图解析器有多少个就加载多少个。
举个简单例子,我们在 SpringMVC 的配置文件中可能像下面这样配置视图解析器:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
默认情况下,这个 bean 的 id 有没有都行,如果有,取什么值都可以,反正最终都是通过类型而不是 id 去查找的视图解析器。但是如果你在 web.xml 中将 detectAllViewResolvers 修改为 false,那么这个 bean 的 id 取值就比较重要了,就一定要是 viewResolver。
如果在 Spring 容器中通过这两种方式(通过类型查找或通过 id 查找)都没有找到 ViewResolver 实例,那么会调用 getDefaultStrategies 方法去获取一个默认的 ViewResolver 实例。默认实例的获取方式如下:
/**
* 为给定的策略接口创建一个默认策略对象列表。
* 默认实现使用“DispatcherServlet.properties”文件(与 DispatcherServlet 类位于同一包中)来确定类名。它通过上下文的 BeanFactory 实例化策略对象。
* 参数:
* context – 当前的 WebApplicationContext strategyInterface – 策略接口
* 回报:
* 对应策略对象的列表
*/
@SuppressWarnings("unchecked")
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
if (defaultStrategies == null) {
try {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return Collections.emptyList();
}
}
这段代码其实也比较简单,就是通过反射去获取默认的视图解析器。
首先给 defaultStrategies 赋值,defaultStrategies 的值实际上就是从 DispatcherServlet.properties 文件中加载到的,我们来看下这个文件内容:

可以看到,这里一共定义了 8 个默认的键值对,有的值是一个,有的值是多个。前面 initStrategies 方法中一共要初始化九个组件,这里默认只定义了 8 个,少了一个 MultipartResolver,这也好理解,并非所有的项目都有文件上传,而且即使有文件上传,用哪一个具体的 MultipartResolver 也不好确定,还是要开发者自己决定。
defaultStrategies 其实加载到的就是这 8 个键值对,其中视图解析器对应的是 org.springframework.web.servlet.view.InternalResourceViewResolver,通过反射创建该类的实例,当 Spring 容器中不存在任何视图解析器的时候,默认的视图解析器即此。
这就是 initViewResolvers 的工作流程,另外 8 个也和它差不多,唯一不同的是 initMultipartResolver,如下:
简化版代码,完整的在上面
private void initMultipartResolver(ApplicationContext context) {
try {
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
}
catch (NoSuchBeanDefinitionException ex) {
this.multipartResolver = null;
}
}
可以看到,它只是根据 bean 的名字去查找 bean 实例,没有去查找默认的 MultipartResolver。
说到这里, 还有 SpringMVC 配置中的细节
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
</bean>
上面这个关于视图解析器和文件上传解析器的配置,有没有注意过,视图解析器的 id 可有可无,而文件上传解析器的 id 必须是 multipartResolver,回顾我们上面的源码分析,你就知道为啥了!
initFlashMapManager
/**
初始化此 servlet 实例使用的FlashMapManager 。
如果没有配置实现,那么我们默认为org.springframework.web.servlet.support.DefaultFlashMapManager 。
*/
private void initFlashMapManager(ApplicationContext context) {
//这个跟上面一样,没用过,不知道是什么。
try {
//public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.flashMapManager.getClass().getSimpleName());
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.flashMapManager);
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
"': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
}
}
}
StandardWrapperValve——invoke
org.apache.catalina.core.StandardWrapperValve#invoke
随着上面的servlet = wrapper.allocate();调用兜兜转转又回到了这个StandardWrapperValve类里,下面就是创建请求的过滤器链
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
看一下这类完整的代码吧,比较长
但是关键的方法调用上面第一次就摘出来了
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Initialize local variables we may need
boolean unavailable = false;
Throwable throwable = null;
// This should be a Request attribute...
long t1=System.currentTimeMillis();
//请求数加1
requestCount.incrementAndGet();
//获取StandardWrapper
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
//获取Context
Context context = (Context) wrapper.getParent();
// Check for the application being marked unavailable
//检查容器是否可用,不可用直接返回异常信息
if (!context.getState().isAvailable()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardContext.isUnavailable"));
unavailable = true;
}
// Check for the servlet being marked unavailable
//检查Servlet是否可用,不可用直接返回异常信息
if (!unavailable && wrapper.isUnavailable()) {
container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
unavailable = true;
}
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
//获取Servlet
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
container.getLogger().error(
sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
} catch (ServletException e) {
container.getLogger().error(sm.getString("standardWrapper.allocateException",
wrapper.getName()), StandardWrapper.getRootCause(e));
throwable = e;
exception(request, response, e);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
throwable = e;
exception(request, response, e);
servlet = null;
}
//为请求设置两个属性
MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
//创建请求的过滤器链
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
//进入这里,调用请求的过滤器链,这里会处理整个请求
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
} catch (ClientAbortException e) {
throwable = e;
exception(request, response, e);
} catch (IOException e) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
throwable = e;
exception(request, response, e);
} catch (UnavailableException e) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
// throwable = e;
// exception(request, response, e);
wrapper.unavailable(e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
// Do not save exception in 'throwable', because we
// do not want to do exception(request, response, e) processing
} catch (ServletException e) {
Throwable rootCause = StandardWrapper.getRootCause(e);
if (!(rootCause instanceof ClientAbortException)) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceExceptionRoot",
wrapper.getName(), context.getName(), e.getMessage()),
rootCause);
}
throwable = e;
exception(request, response, e);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
throwable = e;
exception(request, response, e);
}
//释放内存
// Release the filter chain (if any) for this request
if (filterChain != null) {
filterChain.release();
}
// Deallocate the allocated servlet instance
try {
if (servlet != null) {
// 请求结束,线程回归线程池
wrapper.deallocate(servlet);
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.deallocateException",
wrapper.getName()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
}
// If this servlet has been marked permanently unavailable,
// unload it and release this instance
try {
if ((servlet != null) &&
(wrapper.getAvailable() == Long.MAX_VALUE)) {
wrapper.unload();
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.unloadException",
wrapper.getName()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
}
long t2=System.currentTimeMillis();
long time=t2-t1;
processingTime += time;
if( time > maxTime) maxTime=time;
if( time < minTime) minTime=time;
}
ApplicationFilterFactory——createFilterChain
org.apache.catalina.core.ApplicationFilterFactory#createFilterChain
这个方法主要就是创建过滤器链

/**
* 构造一个 FilterChain 实现,它将包装指定 servlet 实例的执行。
* 参数:
* request - 我们正在处理的 servlet 请求 wrapper – 管理 servlet 实例的包装器 servlet – 要包装的 servlet 实例
* 回报:
* 配置的 FilterChain 实例,如果不执行,则为 null。
*/
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// If there is no servlet to execute, return null
// 如果没有可执行的Servlet直接返回null
if (servlet == null)
return null;
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
// 为req对象设置过滤器链ApplicationFilterChain
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
// 为过滤器链绑定servlet
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// Acquire the filter mappings for this Context
// 从context中取出过滤器,这里可以看出过滤器在context中,即过滤器是在tomcat容器中
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return filterChain;
// Acquire the information we will need to match filter mappings
// 下面为过滤器链添加过滤器,先匹配路径再匹配servletmingc
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
// Add the relevant path-mapped filters to this filter chain
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMap, requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
// 添加过滤器,进去看看
filterChain.addFilter(filterConfig);
}
// Add filters that match on servlet name second
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMap, servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
return filterChain;
}
在创建的过滤器链的时候会传入servlet容器与是否支持异步链,之后便是添加过滤器,filterChain.addFilter(filterConfig);
ApplicationFilterChain——addFilter
org.apache.catalina.core.ApplicationFilterChain#addFilter


ApplicationFilterChain这个过滤器链里我们会用到它很多的方法,需要慢慢来探索
/**
将过滤器添加到将在此链中执行的过滤器集。
参数:
filterConfig – 要执行的 servlet 的 FilterConfig
*/
void addFilter(ApplicationFilterConfig filterConfig) {
// Prevent the same filter being added multiple times
//这里是排重,已经添加了就不用再添加了。这里用的是==比较,直接比较内存地址
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;
//先扩容,在添加
if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
//这里可以看出,后添加的过滤器是在filters的末尾
filters[n++] = filterConfig;
}
ApplicationFilterChain——doFilter
org.apache.catalina.core.ApplicationFilterChain#doFilter
当前面的创建过滤器链完成后就可以去调用过滤器了,也就是回到了StandardWrapperValve——invoke方法,调用到了
filterChain.doFilter(request.getRequest(), response.getResponse());
进入doFilter()方法
/**
* 调用此链中的下一个过滤器,传递指定的请求和响应。如果此链中没有更多过滤器,则调用 servlet 本身的service()方法。
* 参数:
* request - 我们正在处理的 servlet 请求 response – 我们正在创建的 servlet 响应
* 抛出:
* IOException – 如果发生输入/输出错误
* ServletException – 如果发生 servlet 异常
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
(java.security.PrivilegedExceptionAction<Void>) () -> {
internalDoFilter(req,res);
return null;
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
// 这里,跟进去,第一次调用接口时会执行到这里
internalDoFilter(request,response);
}
}
在这个方法里internalDoFilter(request,response);才是最重要的,它才是真正执行过滤器的方法
ApplicationFilterChain——internalDoFilter
org.apache.catalina.core.ApplicationFilterChain#internalDoFilter
//真正执行过滤器的方法
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++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
//这里执行过滤器方法,注意这里的第三个参数this,这个this表示过滤器链,在过滤器执行完后通过这个过滤器链对象执行下一个过滤器,这里用到了职责链模式
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
//所有的过滤器执行完以后,执行servlet的方法
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
//这里调用servlet的service方法,处理真正的请求
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
这里有两个地方很关键
// 链式调用每个Filter的doFilter方法
if (pos < n) {
filter.doFilter(request, response, this);
}
这里会调用过滤器执行过滤
这里的会跳转到OncePerRequestFilter这个抽象类里去执行
org.springframework.web.filter.OncePerRequestFilter#doFilter


这个类是不是很熟悉,它继承了很多的类,而它继承的类里全是Spring的感知类与过滤器类,我们平时写的过滤器就是在这里被执行的,特别是它继承的GenericFilterBean类,太重要了,很多的过滤器都是继承它来写的,比如我们的权限过滤器等等
当执行完调用后就会进入servlet.service(request, response);方法的调用
HttpServlet——service
javax.servlet.http.HttpServlet#service


ApplicationFilterChain中调用servlet.service(request, response);传递的参数是(ServletRequest request, ServletResponse response)。所以执行的是HttpServlet中的service方法.可以看出此方法的目的是将ServletRequest,ServletResponse 转换为我们熟悉的HttpServletRequest ,HttpServletResponse
public abstract class HttpServlet extends GenericServlet {
/**
将客户端请求分派到受保护的service方法。无需重写此方法。
参数:
req – HttpServletRequest对象,其中包含客户端对 servlet 发出的请求 res – HttpServletResponse对象,其中包含 servlet 返回给客户端的响应
抛出:
IOException – 如果在 servlet 处理 HTTP 请求时发生输入或输出错误
ServletException – 如果无法处理 HTTP 请求
也可以看看:
javax.servlet.Servlet.service
*/
//执行HttpServlet.service()方法,做req,res 的强制转换
public void service(ServletRequest req, ServletResponse res){
HttpServletRequest request;
HttpServletResponse response;
try {
//转为 HttpServletRequest,HttpServletResponse
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
随后执行service(request, response);方法调用,这里的调用会先进入FrameworkServlet#service的方法,这里service是重写了父类HttpServlet的service方法
/**
官方doc说得很清楚,复写是为了支持到PATCH请求(PATCH方法是新引入的,是对PUT方法的补充,用来对已知资源进行局部更新,目前使用得非常少,但SpringMVC也给与了支持)
备注:源生的servlet并不支持PATCH请求
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 兼容PATCH方法,非PATCH方法最终还是交给HttpServlet.service()
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
这里做关键的是判断是否是PATCH请求,如果是就直接执行processRequest(request, response);方法,跳过了前面的内容,不然就调用super.service(request, response);重新回到父类HttpServlet的service方法进行判断
// 执行HttpServlet.service此时的参数已经是HttpServletRequest,HttpServletResponse
// 并根据请求方法类型。调用不同的处理方法。子类FrameworkServlet 类中重写了一些方法,所以转去执行子类的doXXX方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
这里我测试的接口是POST接口,所以会进入POST的判断内也就是doPost(req, resp);
doOptions稍微有点特殊,它处理一些是否允许跨域的问题,TRACE请求:主要用于测试或诊断,可忽略
FrameworkServlet——doPost——processRequest
org.springframework.web.servlet.FrameworkServlet#doPost#processRequest
在这里无论上面的方法是走GET,POST,PUT还是PATCH等都会进入processRequest里,processRequest才是最关键的
/**
将 POST 请求委托给processRequest 。
也可以看看:
doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* 处理此请求,无论结果如何都发布事件。
* 实际的事件处理由抽象的doService模板方法执行。
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
// 记录抛出的异常~~~(若有的话)
Throwable failureCause = null;
//拿到之前的LocaleContext上下文(因为可能在Filter里已经设置过了)
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 以当前的request创建一个Local的上下文,后面会继续处理
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 这里面build逻辑注意:previousAttributes若为null,或者就是ServletRequestAttributes类型,那就new ServletRequestAttributes(request, response);
// 若不为null,就保持之前的绑定结果,不再做重复绑定了
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 拿到异步管理器。这里是首次获取,会new WebAsyncManager(),然后放到request的attr里面
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
//这里需要注意:给异步上下文恒定注册了RequestBindingInterceptor这个拦截器(作用:绑定当前的request、response、local等)
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
//这句话很明显,就是吧request和Local上下文、RequestContext绑定
initContextHolders(request, localeContext, requestAttributes);
try {
// 关键在这里
// 模版设计模式:由子类DispatcherServlet去实现实际逻辑
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally { //这个时候已经全部处理完成,视图已经渲染了
// 重置上下文持有者 也就是解绑
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
// 请求完成
requestAttributes.requestCompleted();
}
// 记录结果
logResult(request, response, failureCause, asyncManager);
// 发布请求处理事件,webApplicationContext会发布事件监听
// 关键:不管执行成功与否,都会发布一个事件,说我处理了这个请求(有需要监听的,就可以监听这个事件了,每次请求都会有)
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
long startTime, @Nullable Throwable failureCause) {
//当publishEvents设置为true和 webApplicationContext 不为空就会处理这个事件的发布
if (this.publishEvents && this.webApplicationContext != null) {
// 计算出处理该请求花费的时间
long processingTime = System.currentTimeMillis() - startTime;
this.webApplicationContext.publishEvent(
//ServletRequestHandledEvent这个事件:目前来说只有这里会发布
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, response.getStatus()));
}
}
下面我们来写个监听器,专门来监听这个事件:
/**
* 专门监听ServletRequestHandledEvent时间的监听器
*/
@Slf4j
@Component
public class ServletReqestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent> {
@Override
public void onApplicationEvent(ServletRequestHandledEvent event) {
//url=[/demowar_war/controller/hello]; client=[127.0.0.1]; method=[GET]; servlet=[dispatcher]; session=[null]; user=[null]; time=[143ms]; status=[OK]
log.info(event.getDescription());
log.info("返回状态码为:" + event.getStatusCode()); //返回状态码为:200
log.info("异常信息为:" + event.getFailureCause()); //异常信息为:null
log.info("处理请求耗时为:" + event.getProcessingTimeMillis()); //处理请求耗时为:143
log.info("事件源为:" + event.getSource()); //事件源为:org.springframework.web.servlet.DispatcherServlet@3e7fadbb
}
}
LocaleContext 和 RequestAttributes
LocaleContext 和 RequestAttributes 都是接口,不同的是里边存放的对象不同。
LocaleContext
LocaleContext 里边存放着 Locale,也就是本地化信息,如果我们需要支持国际化,就会用到 Locale。
国际化的时候,如果我们需要用到 Locale 对象,第一反应就是从 HttpServletRequest 中获取,像下面这样:
Locale locale = req.getLocale();
但是大家知道,HttpServletRequest 只存在于 Controller 中,如果我们想要在 Service 层获取 HttpServletRequest,就得从 Controller 中传参数过来,这样就比较麻烦,特别是有的时候 Service 中相关方法都已经定义好了再去修改,就更头大了。
所以 SpringMVC 中还给我们提供了 LocaleContextHolder,这个工具就是用来保存当前请求的 LocaleContext 的。当大家看到 LocaleContextHolder 时不知道有没有觉得眼熟, SecurityContextHolder,这两个的原理基本一致,都是基于 ThreadLocal 来保存变量,进而确保不同线程之间互不干扰。
有了 LocaleContextHolder 之后,我们就可以在任何地方获取 Locale 了,例如在 Service 中我们可以通过如下方式获取 Locale:
Locale locale = LocaleContextHolder.getLocale();
上面这个 Locale 对象实际上就是从 LocaleContextHolder 中的 LocaleContext 里边取出来的。SpringMVC 中还有一个 LocaleResolver 解析器,所以前面 req.getLocale() 并不总是获取到 Locale 的值。
RequestAttributes
RequestAttributes 是一个接口,这个接口可以用来 get/set/remove 某一个属性。
RequestAttributes 有诸多实现类,默认使用的是 ServletRequestAttributes,通过 ServletRequestAttributes,我们可以 getRequest、getResponse 以及 getSession。
在 ServletRequestAttributes 的具体实现中,会通过 scope 参数判断操作 request 还是操作 session
public void setAttribute(String name, Object value, int scope) {
if (scope == 0) {
if (!this.isRequestActive()) {
throw new IllegalStateException("Cannot set request attribute - request is not active anymore!");
}
this.request.setAttribute(name, value);
} else {
HttpSession session = this.obtainSession();
this.sessionAttributesToUpdate.remove(name);
session.setAttribute(name, value);
}
}
可以看到,这里会先判断 scope,scope 为 0 就操作 request,scope 为 1 就操作 session。如果操作的是 request,则需要首先通过 isRequestActive 方法判断当前 request 是否执行完毕,如果执行完毕,就不可以再对其进行其他操作了(当执行了 finally 代码块中的 requestAttributes.requestCompleted 方法后,isRequestActive 就会返回 false)。
和 LocaleContext 类似,RequestAttributes 被保存在 RequestContextHolder 中,RequestContextHolder 的原理也和 SecurityContextHolder 类似。
在 SpringMVC 中,如果我们需要在 Controller 之外的其他地方使用 request、response 以及 session,其实不用每次都从 Controller 中传递 request、response 以及 session 等对象,我们完全可以直接通过 RequestContextHolder 来获取,像下面这样:
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpServletResponse response = servletRequestAttributes.getResponse();
DispatcherServlet——doService
org.springframework.web.servlet.DispatcherServlet#doService
终于我们又回到了DispatcherServlet这个方法,开始了请求分发的内容
/**
* 公开 DispatcherServlet 特定的请求属性并委托给doDispatch以进行实际调度。
* 做请求分发前的准备工作,主要是设置一些请求的相关属性
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 打印请求日志
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
// 如果该请求是include的请求(请求包含) 那么就把request域中的数据保存一份快照版本
// 等doDispatch结束之后,会把这个快照版本的数据覆盖到新的request里面去
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 设置属性
// 说得很清楚,把一些常用对象放进请求域 方便Handler里面可以随意获取
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// 如果是重定向,放置得更多一些
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
try {
// 核心在这里,跟进去
// DispatcherServlet最重要的方法,交给他去分发请求你、找到handler处理等等
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
// 如果是include请求 会上上面的数据快照,重新放置到request里面去
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
if (this.parseRequestPath) {
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
}
DispatcherServlet——doDispatch
org.springframework.web.servlet.DispatcherServlet#doDispatch
最终请求到了DispatcherServlet的doDispatch开启DispatcherServlet 请求分发处理功能
首先根据请求的路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法),
然后匹配路径对应的拦截器,有了HandlerMethod和拦截器构造个HandlerExecutionChain对象。HandlerExecutionChain对象的获取是通过HandlerMapping接口提供的方法中得到。
有了HandlerExecutionChain之后,通过HandlerAdapter对象进行处理得到ModelAndView对象,HandlerMethod内部handle的时候,使用各种HandlerMethodArgumentResolver实现类处理HandlerMethod的参数(非常重要),使用各种HandlerMethodReturnValueHandler实现类处理返回值。 最终返回值被处理成ModelAndView对象,这期间发生的异常会被HandlerExceptionResolver接口实现类进行处理。
/**
* 处理对处理程序的实际调度。
* 将通过依次应用 servlet 的 HandlerMappings 来获取处理程序。 HandlerAdapter 将通过查询 servlet 已安装的 HandlerAdapter 来找到第一个支持该处理程序类的。
* 所有 HTTP 方法都由该方法处理。由 HandlerAdapters 或处理程序自己决定哪些方法是可接受的。
* 参数:
* request – 当前的 HTTP 请求 response – 当前的 HTTP 响应
* 抛出:
* Exception ——在任何类型的处理失败的情况下
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 此处用processedRequest 需要注意的是:若是处理上传,processedRequest 将和request不再指向同一对象
HttpServletRequest processedRequest = request;
// 异常链处理器
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查是否是上传文件
// checkMultipart 这个方法很重要,判断是否是上传需求。
// 如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求
processedRequest = checkMultipart(request);
// 标记一下:是否是文件上传的request了
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 获取当前请求的处理器,很重要,跟进去
// 找到一个处理器,如果没有找到对应的处理类的话,这里通常会返回404,如果throwExceptionIfNoHandlerFound属性值为true的情况下会抛出异常
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 没有对应的处理器直接返回错误
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 通过处理器来获取handler adapter
// 这个HadlerAdapter里面的HandleMethod属性(handle)包含了请求对应的类和方法全路径
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 如果是GET请求,如果内容没有变化的话,则直接返回
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 拦截器前置处理
// 会遍历注册的拦截器Interceptor,执行拦截器上的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 这里是真实的处理请求方法,很重要,跟进去
// 这里会调用AbstractHandlerMethodAdapter的handle方法,这个handle只是一个空壳方法,
// 实际调用的是子类的handleInternal方法,这里的ha是RequestMappingHandlerAdapter类,
// 直接看RequestMappingHandlerAdapter的handleInternal方法
// 真正执行我们自己书写的controller方法的逻辑。返回一个ModelAndView
// 这也是一个很复杂的过程(序列化、数据绑定等等)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 如果异步启动了,这里就先直接返回了,也就不会再执行拦截器PostHandle之类的
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 应用默认视图名称,意思是:如果我们没有设置viewName,就采用默认的。否则采用我们自己的
applyDefaultViewName(processedRequest, mv);
// 拦截器后置处理 应用已注册拦截器的 postHandle 方法。
// 执行所有的拦截器的postHandle方法,并且把mv给他
// 这里有一个小细节:这个时候拦截器是【倒序】执行的
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) { // 这两个catcher什么都不做,只是把异常记录下来
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 这个方法很重要,顾名思义,他是来处理结果的,渲染视图、处理异常等等的
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
checkMultipart
multipartResolver 值是有可能为null的,如果你没有配置对应的Bean的话
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
// 配置了multipartResolver,并且是文件上传的请求 才会继续往下走
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
// 如果该请求已经是MultipartHttpServletRequest 那就输出一个日志走人
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
logger.debug("日志。。。");
} else if (hasMultipartException(request) ) { // 判断是否有MultipartException 一般没有
logger.debug("Multipart resolution failed for current request before - " +
"skipping re-resolution for undisturbed error rendering");
} else {
try {
// 这里特别注意,不管是哪种multipartResolver的实现,内部都是new了一个新的MultipartHttpServletRequest的实现类,所以不再指向原来的request了,所以一定要注意
return this.multipartResolver.resolveMultipart(request);
} catch (MultipartException ex) {
if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
logger.debug("Multipart resolution failed for error dispatch", ex);
// Keep processing error dispatch with regular request handle below
} else {
throw ex;
}
}
}
}
// If not returned before: return original request.
// 如果前面没有返回,就原样返回,相当于啥都不做
return request;
}
这里需要注意的是:org.springframework.web.multipart.support.MultipartFilter,如果在web.xml中配置这个过滤器的话,则会在过滤器中提前判断是不是文件上传的请求,并将请求转换为MultipartHttpServletRequest类型。这个过滤器中默认使用的MultipartResolver为StandardServletMultipartResolver。
在CommonsMultipartResolver中有一个属性叫resolveLazily
private boolean resolveLazily = false;
这个属性值代表是不是延迟解析文件上传,默认为false。最终返回的是一个DefaultMultipartHttpServletRequest的类。这里有一个重要的方法是:parseRequest,这个方法干的事是解析文件上传请求。它的底层是commons-fileupload那一套,不同的是Spring在获取FileItem之后,又进行了一下封装,封装为便于Spring框架整合。

getHandler
/**
返回此请求的 HandlerExecutionChain。
按顺序尝试所有处理程序映射。
参数:
request – 当前的 HTTP 请求
回报:
HandlerExecutionChain,如果找不到处理程序,则返回null
*/
//获取请求的处理器
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//注意,这里的handlerMappings是在第一次请求初始化servlet中初始化的
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
// 会把配置的所有的HandlerMapping 都拿出来查找,只要找到一个就返回
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
SpringMVC默认加载三个请求处理映射类:RequestMappingHandlerMapping、SimpleUrlHandlerMapping、和BeanNameUrlHandlerMapping。
这三个类有一个共同的父类:AbstractHandlerMapping。在上面代码中hm.getHandler(request)这个getHandler方法在AbstractHandlerMapping中,它的子类都没有重写这个方法。因此我们含有必要去AbstractHandlerMapping这个类中看一下这个方法:
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 这个是留给子类去重写实现的:查找handler处理器的~ 比如根据URL去查找匹配等等
// 备注:获取hadnler的过程,非常的复杂,这个必须后面单独的专题再说吧
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);
}
//构建出一个处理器链 注意:和handler绑定了,并且内部还去拿到了所有的拦截器,然后添加到处理器连里面去 getHandlerExecutionChain() 方法自己去看,可以看明白
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//是不是cors请求,cors是跨域请求
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
HandlerExecutionChain#applyPreHandle
这个是前面那段前置拦截器的调用方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
// 注意:如果是拦截器返回了false,就立马触发所有拦截器的AfterCompletion 方法。并且马上return false
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
getHandlerAdapter
这是前面那段获取HandlerAdapter的方法调用
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
/**
返回此处理程序对象的 HandlerAdapter。
参数:
handler -- 要为其查找适配器的处理程序对象
抛出:
ServletException – 如果找不到处理程序的 HandlerAdapter。这是一个致命错误。
*/
//获取处理请求的HandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
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");
}
processDispatchResult
这是前面那段处理结果的调用
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//如果有异常,就进入异常处理逻辑,返回到异常页面
if (exception != null) {
// 含有异常页面视图的异常
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//1、会执行所有的我们的自己配置(或者默认配置)了的HandlerExceptionResolver处理器
//2、上面需要注意了,但凡处理方法返回的不是null,有mv的返回。那后面的处理器就不会再进行处理了。具有短路的效果,一定要注意 是通过null来判断的
//3、处理完成后,得到error的视图mv,最后会设置一个viewName,然后返回出去
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 若视图不为空,不为null,就开始执行render()方法,开始渲染视图了
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
// 如果有错误视图,这里清除掉所有的请求域里的所有的错误属性
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
//处理异步=========我们发现,它不执行后面的AfterCompletion方法了,注意一下即可
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// 执行拦截器的AfterCompletion 方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
剩一个视图渲染的方法:render()
render(mv, request, response);
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 通过localeResolver吧local解析出来,放到response里面去
Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
//==================视图:关键中的关键==================
View view;
String viewName = mv.getViewName();
// 如果已经有viewName了(绝大多数情况)
if (viewName != null) {
// 视图解析器 根据String类型的名字,解析出来一个视图(视图解析器有多个)
// 还是那个原理:只要有一个返回了不为null的,后面的就不会再解析了
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// 如果解析不出来视图,那就抛出异常,说不能解析该视图
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
} else { //没有视图名称,但是必须有视图内容,否则抛出异常
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
try {
//设置响应马 status
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 根据model里的数据,正式渲染(关于此部分逻辑,后续再说,也比较复杂)
view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
throw ex;
}
}
上面的方法都是在DispatcherServlet类中的方法,只是小插曲,最重要的还是mv = ha.handle(processedRequest, response, mappedHandler.getHandler());这个调用
在上面的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());返回了HandlerAdapter接口,而ha.handle()方法就是HandlerAdapter接口所提供的

来了解一下这个接口

可以看待这个接口是SPI接口,所谓的SPI就是API接口理解的反过来。

有很多的类实现了这个接口,而我们暂时只需要关注第一个实现类即可
这里我们总结一下关于doDispatch的大致流程

RequestMappingHandlerAdapter——handleInternal
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal


其实上面的mv = ha.handle(processedRequest, response, mappedHandler.getHandler());调用的是AbstractHandlerMethodAdapter抽象类里的handle方法,handle又调用了handleInternal方法,但是这个抽象类里的方法都是空的,实际上实现的还是它的实现类RequestMappingHandlerAdapter里的handleInternal


@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
//没有session,完全不需要会话同步,进入这里。跟进去
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
RequestMappingHandlerAdapter——invokeHandlerMethod
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
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);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//重点在这里,这里调用的是ServletInvocableHandlerMethod的invokeAndHandle方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
// 发出请求已完成的信号。
// 在请求完成后执行所有已注册执行的回调。
// 更新在请求处理期间已访问的所有会话属性
webRequest.requestCompleted();
}
}
这里配置很多参数进行调用和处理
ServletInvocableHandlerMethod——invokeAndHandle
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle


/**
通过配置的HandlerMethodReturnValueHandlers之一调用方法并处理返回值。
参数:
webRequest – 当前请求 mavContainer – 此请求的 ModelAndViewContainer providedArgs – 按类型匹配的“给定”参数(未解析)
*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//这个方法,跟进去
//这里调用的是父类 InvocableHandlerMethod 的 invokeForRequest 方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置响应状态
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
InvocableHandlerMethod——invokeForRequest
org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest


/**
在给定请求的上下文中解析其参数值后调用该方法。
参数值通常通过HandlerMethodArgumentResolvers解析。然而, providedArgs参数可以提供要直接使用的参数值,即没有参数解析。提供的参数值的示例包括WebDataBinder 、 SessionStatus或抛出的异常实例。在参数解析器之前检查提供的参数值。
委托getMethodArgumentValues并使用已解析的参数调用doInvoke 。
参数:
request – 当前请求 mavContainer – 此请求的 ModelAndViewContainer providedArgs – 按类型匹配的“给定”参数,未解析
回报:
调用方法返回的原始值
抛出:
Exception – 如果找不到合适的参数解析器,或者方法引发异常,则会引发异常
也可以看看:
getMethodArgumentValues , doInvoke
*/
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取参数,先跳过
// 把HTTP请求的数据绑定到控制器的入参上
// 值得注意的是,如果你在控制器入参上定义了@PathVariable或者@RequestBody需要被赋值的话,它会在getMethodArgumentValues方法中完成。
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//这里,跟进去,调用控制器方法进行业务逻辑处理
return doInvoke(args);
}
InvocableHandlerMethod#getMethodArgumentValues#resolveArgument
/**
* 遍历已注册的HandlerMethodArgumentResolvers并调用支持它的那个。
* 抛出:
* IllegalArgumentException – 如果没有找到合适的参数解析器
*/
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
// 由对应的解析器从请求中解析参数
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
HandlerMethodArgumentResolver是一个接口


这个接口有很多的实现类,这里就做个例子
@PathVariable,会由PathVariableMethodArgumentResolver进行处理
@RequestBody,会由RequestResponseBodyMethodProcessor进行处理
以RequestResponseBodyMethodProcessor为例,它会将HTTP请求消息转换成一个对象,
绑定到对应的请求参数上(会执行必要的格式转换,将String转换成Integer、Double),并且进行一系列基于@java.validation.Valid的数据有效性验证操作(长度,格式等等),最终将绑定/验证结果存储在bindingResult中。
/**
* 如果验证失败,则抛出 MethodArgumentNotValidException。
* 抛出:
* HttpMessageNotReadableException – 如果RequestBody.required()为true并且没有正文内容,或者没有合适的转换器来读取内容。
*/
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 使用消息转换器阅读,对象入参
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
// 进行数据验证
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
最后在完成这一些列操作返回Object[]args后,就开始执行doInvoke(args),方法中使用到了java中的反射机制,会直接试图调用
doInvokemethod.invoke(getBean(),args)来执行相应的控制器方法。
/**
* 使用给定的参数值调用处理程序方法。
*/
@Nullable
protected Object doInvoke(Object... args) throws Exception {
Method method = getBridgedMethod();
// 这里的getBridgedMethod方法就是获取url对应的方法
ReflectionUtils.makeAccessible(method);
try {
// 判断是否是Kotlin的挂起函数
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
}
// 这里通过反射调用方法,这一步就直接进入Controller对应的方法去了
// 至此整个请求处理完成,核心在于从mapping中获取对应的handler
return method.invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(method, getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}

后面接口运行的结果被捕获在ServletInvocableHandlerMethod#invokeAndHandle中的returnValue对象上,并由预先注册的returnValueHandlers进行返回值的进一步处理
源代码上面有
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
Spring框架中有多个不同的returnValueHandler,程序会根据控制器方法返回类型选择合适的handler。
比如说,在MVC中,如果我们返回了一个String,那么会由ViewNameMethodReturnValueHandler来进行处理。

在ViewNameMethodReturnValueHandler中,并不会做实际的视图渲染,
而是将returnValue设置在了mavContainer的viewName成员变量上。
之后会判断是否是RedirectView(比如redirect:/login),设置redirectModelScenario的值。在此我们可以推测,真正的ModelAndView的获取和生成,是由mavContainer负责的。
通过handleReturnValue方法来设置mavContainer的值
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler#handleReturnValue
/**
* 通过向模型添加属性并设置视图或将ModelAndViewContainer.setRequestHandled标志设置为true以指示已直接处理响应来处理给定的返回值。
* 参数:
* returnValue – 从处理程序方法返回的值 returnType – 返回值的类型。此类型必须先前已传递给必须返回true的supportsReturnType 。 mavContainer – 当前请求的 ModelAndViewContainer webRequest – 当前请求
* 抛出:
* Exception – 如果返回值处理导致错误
*/
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
// 设置mavContainer中的ViewName
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
// 如果是RedirectView,那么设置重定向
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
在上一步执行并由returnValueHandler将视图名称viewName写入mavContainer之后, 代码回到RequestMappingHandlerAdapter中,在这里getModelAndView方法会负责返回mav。 视图对象的赋值关键在于1008行ModelAndView的构造函数,在其中时直接将viewName赋值给了view, 那随后就会由DispatherServlet中的视图解析器ViewResolver进行处理
getModelAndView
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getModelAndView
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
// 通过构造函数创建一个新的视图对象
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
对象ModelAndView构造函数
org.springframework.web.servlet.ModelAndView
/**
* 给定视图名称、模型和 HTTP 状态,创建一个新的 ModelAndView。
* 参数:
* viewName – 要渲染的视图的名称,由 DispatcherServlet 的 ViewResolver 解析 model ——模型名称(字符串)到模型对象(对象)的映射。模型条目可能不是null ,但如果没有模型数据,模型 Map 可能为null 。 status – 用于响应的 HTTP 状态代码(在视图呈现之前设置)
*/
public ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status) {
this.view = viewName;
if (model != null) {
getModelMap().addAllAttributes(model);
}
this.status = status;
}
返回了ModelAndView,就结束了DispatcherServlet中相应handlerAdapter的handle方法(1063行), 接下来,就到了视图解析的部分了
源码上面也有
org.springframework.web.servlet.DispatcherServlet#doDispatch
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
这里的入参有五个:
- HTTP请求
- HTTP响应
- MappedHandler – 匹配到的handler
- ModelAndView– 通过前述handler调用所返回的ModelAndView
- Exception
前述处理过程中所返回的异常会在这个方法中被handler再度解析成一个对应的ModelAndView, 也就是我们在浏览器端所看到的错误信息(DispatcherServlet.java:1134)。 然后就在1141行调用了DispatcherServlet上的render方法,对正常/异常的视图进行渲染
在render方法中,分为下列三步
- 设置正确的Locale地区
- 根据视图名称和Locale来解析视图
- 由模板引擎进行视图渲染
关于render的代码也在上面
org.springframework.web.servlet.DispatcherServlet#processDispatchResult
render(mv, request, response);
/**
* 渲染给定的 ModelAndView。
* 这是处理请求的最后阶段。它可能涉及按名称解析视图。
* 参数:
* mv - 要渲染的 ModelAndView request – 当前的 HTTP servlet 请求 response – 当前的 HTTP servlet 响应
* 抛出:
* ServletException – 如果视图丢失或无法解析
* Exception ——如果渲染视图有问题
*/
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;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 我们需要解析视图名称
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
// 无需查找:ModelAndView 对象包含实际的 View 对象。
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 委托给 View 对象进行渲染。
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
逻辑比较直观,重点在于resolveViewName这一步。 Spring中默认会配置多个ViewResolver进行遍历操作,找到第一个匹配的view。 而在众多viewResolvers中,首先遍历到的是ContentNegotiatingViewResolver。 它本身不会负责视图解析,而是会委派给从应用上下文获取到的不同ViewResolver,通过对HTTP 请求头Accept字段的比对,找到一个匹配的解析器进行解析。 那么在SpringMVC + Thymeleaf的架构上,ContentNegotiatingViewResolver就将解析视图的任务委派给了ThymeleafViewResolver,由它解析出一个ThymeleafView并返回。
那么在最后view.render(mv.getModelInternal(), request, response)的操作中,ThymeleafView会根据ModelAndView中定义的Model(填充字段的映射), 将渲染后的数据写入HttpServletReponse,并由Servlet容器返回给客户端,结束整个请求的操作。
最后一步一步的返回到上面的类去最后回到DispatcherServlet类里,然后又返回到起点StandardWrapperValve里的invoke方法里去
最后调用servlet = wrapper.allocate();将此先前分配的 servlet 返回到可用实例池。也就是线程回归线程池,源码在上面一开始就贴出来了
StandardWrapper——deallocate
org.apache.catalina.core.StandardWrapper#deallocate
/**
* 将此先前分配的 servlet 返回到可用实例池。如果这个 servlet 类没有实现 SingleThreadModel,则实际上不需要任何操作。
* 参数:
* servlet – 要返回的 servlet
* 抛出:
* ServletException – 如果发生释放错误
*/
@Override
public void deallocate(Servlet servlet) throws ServletException {
// If not SingleThreadModel, no action is required
// 如果不是 单线程模型,则不需要任何操作
if (!singleThreadModel) {
// 以原子方式将当前值减一。
countAllocated.decrementAndGet();
return;
}
// Unlock and free this instance
// 解锁并释放此实例
synchronized (instancePool) {
countAllocated.decrementAndGet();
instancePool.push(servlet);
instancePool.notify();
}
}
最后的最后当StandardWrapperValve的invoke()方法结束时,就会回到StandardContextValve的invoke()方法中

StandardContextValve的invoke()方法中这一段代码调用了MVC的执行,一切都是从这开始。
wrapper.getPipeline().getFirst().invoke(request, response);
无论是StandardWrapperValve还是StandardContextValve他们都是Tomcat的包下的类,都是实现了Valve这个接口,而invoke()也是Valve接口的方法。



再前面就是Tomcat的源码,不在这次MVC的范围里,从这次的探究中发现,MVC中增加一些关于异步的判断和支持与Kotlin的支持,不过这里没做深究,只是通过一个最简单的接口来一步一步往下走。
SpringMVC 的初始化流程,主要涉及到了 HttpServletBean、FrameworkServlet 以及 DispatcherServlet 三个实例,HttpServletBean 主要是加载 Servlet 配置的各种属性并设置到 Servlet 上;FrameworkServlet 则主要是初始化了 WebApplicationContext;DispatcherServlet 则主要是初始化了自身的九个组件。
一张概览图,包含了大部分关键的内容,手绘版本,配合上面代码分析最佳

SpringMVC上下文
上面的分析的SpringMVC与Tomcat相关的都是基于SpringBoot自动配置好的内容,主要分析了请求过来是MVC是如何工作的。

在传统的SpringMVC中有两个上下文,做到了上下文隔离,但是这么做会有问题,就是开发者会分不清到底创建的Bean放在哪个上下文,其实如果DispatcherServlet在Servlet找不到Bean还是会去Root上找Bean,最简单的方法就是将所有的内容都放在Root上下文里,SpringBoot就是这么做的。
SpringBoot在加载时会绕过Servlet来加载,因为在SpringBoot的jar包与war包部署时有区别,在war包上会先启动Servlet服务器再启动SpringBoot的IOC容器,jar包部署时会直接启动SpringIOC容器然后再启动Serlvet容器。
如果在war包时需要使用SpringBoot的监听器等,可以直接注册等对应的Bean,交由ServletWebServerApplicationContext管理
这两篇对此都有相关的介绍
