Spring MVC源码——初始化及请求处理
本文将从SpringMVC的初始化及如何处理一个Get请求来介绍源码。
Spring MVC的初始化
基于对tomcat和javaweb的认识, web容器在初始化时将会调用Servlet提供的init方法。从这个角度入手,再加上我们对SpringMVC使用时配置web.xml的经验,可以得知我们阅读源码要熟悉的第一个类就是DispatcherServlet:
<servlet>
<servlet-name>springMVC</servlet-name>
<!--基于springmvc的web.xml配置所依赖的处理器-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
DispatcherServlet
DispatcherServlet作为HttpServlet的子类,我们不难想到在容器初始化时其init方法将会被调用,而经过查找可以发现, 初始化时调用的init()方法位于其父类HttpServletBean中:
@Override
public final void init() throws ServletException {
// 省略部分代码...
initServletBean();
}
这里省略部分代码,只看最后一行的调用,发现其调用了子类FrameworkServlet重写的initServletBean方法:
@Override
protected final void initServletBean() throws ServletException {
// 省略...
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
// 省略...
}
这里只需要关注**initWebApplicationContext()**方法即可:
protected WebApplicationContext initWebApplicationContext() {
// 省略...
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
// 省略...
return wac;
}
这里关注一下WebApplicationContext的创建过程(createWebApplicationContext方法):
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 省略...
configureAndRefreshWebApplicationContext(wac);
return wac;
}
然后接下来是configureAndRefreshWebApplicationContext方法
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// 省略...
// 这里添加了一个监听
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 省略...
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
这里主要做两件事:
- 添加一个事件监听ContextRefreshListener
- 调用ApplicationContext的refresh方法
这里还是先看最后一行,一个比较熟悉的方法——refresh()。没错,这里要初始化Spring上下文了,下面是几个很熟悉的代码,这里就不详细介绍了,后面讨论Spring源码的时候会讲:
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
到这里可能会好奇,并没有看到SpringMVC相关类的初始化,这是为什么呢?
别急,前面还有一个事件监听没有关注。我们知道Spring在发布某些事件时,会由事件派发器去给相应的事件监听去派发(执行)事件,我们就从这里入手。
回到DispatcherServlet的父类FrameworkServlet中,其内部存在一个类ContextRefreshListener,作为一个事件监听存在,所以当ContextRefreshedEvent这个事件发布时, 其onApplicationEvent方法便会被触发。
ContextRefreshListener
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
// org.springframework.web.servlet.FrameworkServlet#onApplicationEvent
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
深入FrameworkServlet的onApplicationEvent方法,最终会调用到由子类DispatcherServlet重写的onRefresh方法:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
在initStrategies方法中,执行了一些列的初始化操作,内容如下:
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
各个组件的作用:
MultipartResolver:用于处理文件上传服务,如果有文件上传,那么就会将当前的HttpServletRequest包装成DefaultMultipartHttpServletRequest,并且将每个上传的内容封装成CommonsMultipartFile对象。
LocaleResolver:处理应用的国际化问题
ThemeResolver:主题解析
HandlerMapping:处理器映射器,负责将请求映射到具体的处理器。
HandlerAdapter:处理器适配器,调用具体的方法对用户发来的请求来进行处理。当HandleMapping获取到执行请求的Controller时,DispatcherServlet会根据controller对应的类型来调用相应的处理器适配器处理。
HandlerExceptionResolver:解析请求处理过程中所产生的异常。
RequestToViewNameTranslator:就是将请求的url解析成一个viewname的解析器。具体怎么个算法解析,可以自定义,也可以使用默认的实现类。当我们的controller的方法中,没有返回值时(只考虑返回值为jsp页面的名字,不考虑直接输出字符串到浏览器),那么便会调用Translator来获取一个默认的viewname,然后使用这个viewname的值,去jsp目录中找与之同名的jsp页面,如果还找不到,那就显示404了。
ViewResolver:是根据视图名和Locale解析出视图。在传统的web项目中,jsp,ftl等页面的渲染生成都是由ViewResolver来完成的。但是随着前后端分离项目的流行,该组件的使用已经越来越少。
FlashMapManager:FlashMapManager用于获取重定向之前的一些信息,FlashMap 借助 session 重定向前通过 FlashMapManager将信息放入FlashMap,重定向后 再借助 FlashMapManager 从 session中找到重定向后需要的 FalshMap。
以一个GET请求为例看请求的过程
这里重点关注请求处理的核心流程,所以只贴出部分代码。
FrameworkServlet.doGet
首先是FrameworkServlet的doGet方法:
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
// org.springframework.web.servlet.FrameworkServlet#processRequest
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 省略...
try {
doService(request, response);
}
// 省略...
}
这里的doService方法是一个抽象方法,具体的实现在DispatcherServlet中:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 省略...
try {
doDispatch(request, response);
}
// 省略...
}
这里只关注一个核心的处理逻辑,就是调用自己的doDispatch方法:
// Spring MVC的最核心代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//创建视图对象
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查是不是文件上传请求(根据contentType判断)
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 把请求交给handlerMapping处理,获取处理器和拦截器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 找不到的话就报异常
noHandlerFound(processedRequest, response);
return;
}
// 获取处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 获取请求类型
String method = request.getMethod();
//get方法为true
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
// 省略部分代码...
}
// 循环调拦截器对应的 preHandle 方法 如果有拦截器返回false这里就会返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) { // ===> applyPreHandle
return;
}
// 这里通过处理器适配器去执行方法 通过反射调用
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 设置默认视图,当返回了mav,但是又没设置具体view的时候
applyDefaultViewName(processedRequest, mv);
// 调用拦截器的后置方法 postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
// 省略...
}
// 执行结果处理逻辑
//1,请求视图解析器,解析成view
//2,执行页面渲染(jsp)
//3,调用拦截器afterCompletion方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); // ===> go!
} catch (Exception ex) {
// 省略...
// ...
}
这里大致流程如下:
- 判断是否文件上传请求,如果是的话就把请求封装成MultipartHttpServletRequest
- getHandler方法:通过HandlerMapping构建处理器:包含我们的Controller中的指定方法(HandlerMethod)和拦截器
- getHandlerAdapter方法:获取处理器适配器
- applyPreHandle方法:调用拦截器链的preHandle方法,期间如果拦截器返回false就返回
- handle方法:调用处理器(也就是我们定义的Controller)的指定方法(反射)来处理请求
- applyPostHandle方法:调用拦截器链的portHandle方法,期间如果拦截器返回false就返回
- processDispatchResult方法:渲染视图、调用拦截器afterCompletion方法
总结
最后汇总一下整个请求的处理流程:
- tomcat监听的端口到http请求之后交给Serlvet处理
- DispatchServlet最终收到请求之后开始处理
- DispatcherServlet调用处理器映射器HandlerMapping根据路径参数来获取处理器(我们的Controller方法)和拦截器
- DispatcherServlet获取处理器适配器用于后续执行处理器方法
- DispatcherServlet调用拦截器的preHandle链路处理
- 处理器适配器HandlerAdapter执行我们的业务代码
- DispatcherServlet调用拦截器的postHandle链路处理
- 视图解析器处理结果
- 调用拦截器的afterCompletion链路处理
- 响应用户