本文将分析Spring MVC是如何处理请求的;
首先,分析HttpServletBean,FrameworkServlet和DispatcherServlet这三个Servlet的处理过程;这样大家就可以明白从Servlet容器将请求交给Spring MVC一直到DispatcherServlet具体处理请求之前都做了什么;
最后再重点分析Spring MVC中最核心的处理方法doDispatch的处理流程;
HttpServletBean
它主要参与了创建工作,并没有涉及到请求的处理;
FrameworkServlet
Servlet的处理过程:首先从Servlet接口的service方法开始,然后在HttpServlet的service方法中根据请求的类型不同将请求路由到了doGet,doHead,doPost,doPut,doDelete,doOptions和doTrace这7个方法;
HttpServlet默认实现了doHead,doOptions和doDelete;
FrameworkServlet重写了service以及除了doHead的所有其他6个方法;
在service方法中增加了对PATCH类型请求的处理,其他类型的请求直接交给父类进行处理;doOptions和doTrace方法可以通过设置dispatchOptionsRequest和dispatchTraceRequest参数决定是自己处理还是交给父类处理(默认是交给父类,doOptions会在父类的处理结果中增加PATCH类型);所有需要自己处理的请求都交给processRequest方法统一处理;
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String method = request.getMethod();
//对PATCH类型请求的处理
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
protected final void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException {
processRequest(request,response); //别的需要自己处理的方法都与doGet类似
}
这里的处理思路和HttpServlet的相反,它是将所有的请求都合并到了processRequest方法,不同类型的请求用不同的Handler进行处理;
为什么要这样做呢,不再调用super.service(),而是直接将请求交给processRequest处理不是更简单吗?从结构上来看确实如此,但是如果这样做的话会存在一些问题;
例如:我们为了某种特殊需求需要在Post请求处理前对request进行一些处理,这时可能会新建一个继承自DispatcherServlet的类,然后覆盖doPost方法,在里面对request进行处理,然后再调用super.doPost方法,但是父类根本没有调用doPost,这时就会出现问题,虽然有其他方法来解决这个问题,但按正常的逻辑,调用doPost应该可以完成才合理,而且一般情况下开发者并不需要对Spring MVC 的内部结构非常了解。所以Spring MVC的这种做法是有必要的;
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取LocaleContextHolder中原来保存的LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//获取当前请求的LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
//获取RequestContextHolder中原来保存的RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//获取当前请求的ServletRequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//将当前请求的LocaleContext和ServletRequestAttributes设置到LocaleContextHolder和RequestContextHolder中
initContextHolders(request, localeContext, requestAttributes);
try {
//核心语句,这是一个模板方法,在DispatcherServlet中具体实现
//在这之前和之后都做了一些事情,processRequest整个方法就是我们熟知的装饰模式
doService(request, response); /////////////////////////////////////////////////////
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//恢复原来的LocaleContext和ServiceRequestAttributes到LocaleContextHolder和RequestContextHolder,
//避免影响Servlet以外的处理,如Filter
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (l