此篇文章是个人通过阅览Spring MVC源码的学习过程记录,包含Spring MVC的关键细节源码设计和一些设计上的tips,更近似于一种意识流的记录方式,锚点设置可能也有些乱,零零散散的点我日后有时间会统一总结起来。
Http的八种请求方式
Restful风格的Http有八种请求方式,除了最常使用的Get与Post还有Head、Put、Delete、Options、Trace、Connect。
在Restful接口的设计中,请求方方式的语义性很强,我们时常用他约束接口请求的行为,请求类型的语义:
OPTIONS获取服务器支持的HTTP请求方法;
HEAD跟get很像,但是不返回响应体信息,用于检查对象是否存在,并获取包含在响应消息头中的信息。
GET向特定的资源发出请求,得到资源。
POST向指定资源提交数据进行处理的请求,用于添加新的内容。
PUT向指定资源位置上传其最新的内容,用于修改某个内容。
DELETE请求服务器删除请求的URI所标识的资源,用于删除。
在这里暂且不谈很少用到的Trace和Connect。
数据的幂等性
多次执行相同的方法,会返回同样的结果,这时我们说方法具有幂等性,对于接口同理,进行多次重复请求后台会进行同样的操作,返回同样的结果,这个接口便具有幂等性。
设计良好的api的PUT和PATCH应是具有幂等性的,而PUSH则相反,GET需要根据具体的业务来定性。
下文中的DispatchServlet中的patch
相关继承关系
这里涉及到的处事方法广泛用到了模板方法设计模式。譬如HttpServletBean的init()中执行的initServletBean()方法定义是在HttpServletBean中,而实现是在FrameworkServlet中的。以下是相关的继承图,我已用@标识了没有定义实现细节的模板方法。

web.xml中:
<web-app> <servlet> <servlet-name>example</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>example</servlet-name> <url-pattern>/example/*</url-pattern> </servlet-mapping> </web-app>
每个前端请求都对应着servlet的service方法,调用后根据请求类型(GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE)调用对应的方法,除PATCH外的方法会转发至对应的方法中(根据HttpServlet类中的service方法),譬如doGet(),从而调用doService()方法(因为servlet请求类型中没有PATCH,所以无需多余写一个doPatch来处理它),方法中会跳转至核心方法doDispatch(),接下来逐个降价其中的一些相关代码。
FrameworkServlet
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 不管是什么方法(doPost、doDelete) 重写HttpServlet的方法都是这一行代码
processRequest(request, response);
}
/**
* Override the parent class implementation in order to intercept PATCH requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
//跳转到了toGet toPost等
super.service(request, response);
}
}
核心方法processRequest,被定义成了final的,这里面的LocaleContext存放了一些本地化的信息,而RequestAttribute是负责管理session和request的属性(Attribute)的接口,ServletRequestAttributes是它的一种具体实现。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//分别获取现有的和新生成的LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
//分别获取现有的和新生成的RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
//方法入口 模板方法
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//还原原有的LocaleContext和RequestAttributes
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
//置为不可操作
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
//发布消息,是一个Event 可以被监听器监听
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
tip:使用监听器监听发布事件
上面方法中的publishRequestHandledEvent实现如下:
private void 取publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
long startTime, @Nullable Throwable failureCause) {
if (this.publishEvents && this.webApplicationContext != null) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
//发布Event
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, response.getStatus()));
}
}
通过继承ApplicationListener,可以监听以上的事件:
public class Listener implements ApplicationListener<ServletRequestHandledEvent>{\
...
@Override
public void onApplicationEvent(ServletRequestHandledEvent event){
logger.info(event.getDescription);
}
}
tip:维持原有设计,保持逻辑结构
利用servlet的service跳跃到相应的请求方法,最后其实还是映射到processRequest方法中,这是该类的核心方法,这里之所以采用这种方式,而不是直接在service方法中将所有请求直接映射到processRequest方法,一是因为部分方法有自己的处理(如doTrace、doOptions),二也是为了良好的扩展性,如果直接将所有的请求统一处理,那么暗请求类型区分的doGet,doPost等方法将没有执行的机会。在进行功能扩展时,介于开发者不需要知道项目实现逻辑细节的原则,可能会引发种种问题(譬如继承了DispatcherServlet方法想要覆盖doPost方法作为代理预先处理请求内容,但实际上只是覆盖了HttpServlet中的方法,并且运用mvc框架时请求并没有被正确代理,因为它根本就没有通过该方法,而是被无关请求方式统一的处理了),会在造轮子系列详细解释这一点:造轮子0 浅谈设计模式-鱼鱼的博客
DispatcherServlet
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//方法中打印request相关信息的日志
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请求保留快照备份
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));
}
}
}
// 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);
}
try {
//核心代码只有这一行
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
//include快照还原
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
这里包含了打印日志、保存快照、设置属性、doDispatch方法入口。
其中设置Context和Resolver和themeSource属性是在handler和view中使用的,而flashMap相关的属性是用于转发(Redirect)请求的参数传递。
doDispatch
doDispatch方法的代码,算是流程中最核心的代码了:
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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 {
//如果是multipart(如文件上传),处理request,后面将通过MultipartResolver解析
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//请求到处理器(Controller)的映射,通过HandlerMapping进行映射
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//将处理器包装成相应的适配器(从而支持多种类型的处理器)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//如果处理程序支持,处理header中包含的last-modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//1 拦截器预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//实际的handle调用,即进入控制层,返回ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//并发情况,异步已被其他线程处理
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//
applyDefaultViewName(processedRequest, mv);
//拦截器后处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
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);
}
}
}
}
解析:1处是扫描拦截器的过程,在进入控制层前进行预处理,若是拦截器运行中返回false会直接按请求链返回,即下面代码中的interceptor.preHandle方法:
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];
//执行拦截
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
2是WebAsyncManager中的方法,针对请求的异步处理判断时候
参考链接:芋道源码

