阅读完需:约 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
管理
这两篇对此都有相关的介绍