关于DispacherServlet的请求流程,每天都在用,但其内部的细节,了解多少呢?
Servlet介绍
DispacherServlet也是Servlet的一种,想弄明白它,对Servlet要有一定的了解。Servlet是一套规范,我们按照规范编写的代码可以直接在Servlet容器内运行。但我们先从高层讲起,假设读者对Servlet心里有个大概。
完整流程
- 初始化操作,在容器启动的时候,首先进入Servlet的init(ServletConfig config)方法,这是的Servlet接口的基本方法。所有Servlet启动的时候都要有的。
在Servlet的API中,其交给了GenericServlet来实现,而GenericServlet将其交给init()无参方法实现。
DispacherServelt中的init方法使用的是其父类HttpServletBean的init方法。我们来看看HttpServletBean的初始化做了什么事情。
/**
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*
* 映射Servlet的配置参数到Bean的属性上,然后交给其子类进行初始化。如果子类初始化失败,则抛出ServletException。
*/
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
PropertyValues pvs = new 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.
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
解释:
- 首先获取配置,如果再Servlet容器,比如说Tomcat中,获取配置的Servlet参数,我在web.xml中配置了一个采纳数,经过new ServletConfigPropertyValues之后,获取到了参数。
关于属性非空后,内部如何处理,后期再深入研究,现在往下看,进行initServletBean()操作
![img_fbaef4674628759135c5bb2bc3a0687f.png](https://i-blog.csdnimg.cn/blog_migrate/f16e07b3aafb2d6610081e27a6f5c124.png)
![img_5ddefefb31b04ebd4ce718562094dc78.png](https://i-blog.csdnimg.cn/blog_migrate/aaa1f8bc2d3dc89c44ef9adc379da3a3.png)
- 初始化ServletBean,这一步进入到了FrameworkServlet中。在HttpServlet设置属性之后调用,创建Servlet的WebApplicationContext。然后初始化ServletFramework,初始化FramworkServlet是个空方法,留待自己补充。
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
// 初始化WebApplication环境
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
初始化initWebApplicationContext,又包含了很多的内容,这个留着下次再说,先把大体思路搞清楚。先给读者瞅一眼初始化的操作
![img_cea5c96bd81fa7f227e7d38b4608b748.png](https://i-blog.csdnimg.cn/blog_migrate/a3c81cbff01c919c718a5c1ec66d3de5.png)
到这一步,DispacherServlet的初始化工作算是完成了,听名字可以理解为什么叫做DispacherServlet了,因为它并没有做初始化的工作,它主要负责分发(Dispacher)请求和响应。
- 处理真正的请求。我们知道Servlet处理请求,调用的是service方法。DispacherServlet的service(ServletRequest req, ServletResponse res),由HttpServlet来处理,其把请求和响应分别封装为HttpServletRequest,HttpServletResponse。
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
- 处理service(HttpServletRequest req, HttpServletResponse resp),HttpServlet的子类覆盖了这个方法,因此会先调用FrameworkServlet的service方法。
FrameworkServlet的service方法主要拦截Patch方法或者null的HttpMethod
/**
* 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 {
// 交给父类处理
super.service(request, response);
}
}
如果FrameworkServlet收到的是常规的HTTP方法,再转给父类HttpServelt来处理,HttpServlet的 service(HttpServletRequest req, HttpServletResponse resp)只是判断HttpMethod,然后进行相应的doXXX操作,doGet,doPost,doPut等。瞅一眼看看
![img_6d6862105813a65c5fd66133f4c72082.png](https://i-blog.csdnimg.cn/blog_migrate/21bca6d4469320dced7fed9024d8c679.png)
- 上一步提到,所有的HttpServlet将方法转给doXXX来操作,其父类FrameworkServlet将所有的doXXX操作,又交给了processRequest来处理,感受下。
![img_70685e63ec24d8262051272aec725e7e.png](https://i-blog.csdnimg.cn/blog_migrate/79e8a5d4e598e105ead275709a56da0c.png)
那么真正这一步要看的是processRequest做了什么操作。处理请求,不管处理结果如何都会发布事件,发布的事件由doService模板方法处理。
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// i18n的操作
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
// 获取绑定到当前线程的请求属性
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 创建Servelet请求属性,这些都是在Spring框架中的内容
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 获取当前请求的asyncManager
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
//注册请求的拦截器
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//初始化ContextHodlers
initContextHolders(request, localeContext, requestAttributes);
try {
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 {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
- DispatcherServlet的doService方法,上步说到FrameworkServlet的processRequest一定会调用doService方法,而FrameworkServlet自己并没有实现doService方法,最终交给了DispatcherServlet来实现。
我们看看DispatcherServlet的处理,终于等到真正关键的类出场了,进去之后发现,这个类做的事情都是设置属性,最后转给doDispatch处理。
// 暴露DispatcherServlet-specific的请求属性给doDispatch来处理
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
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());
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.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
-
doDispatch处理真正的分发到处理器Handler上面,handler会按顺序获得servlet的HandlerMappings,然后获得HandlerAdapter会查询第一个支持的handler类。
所有的Http方法都由这个方法来处理,
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 记得上上一步是processRequest处理的
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//再次获取
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查是否为文件类型请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//获取当前请求的Handler, 方法为循环遍历handlerMappings,然后找到request对应的handler。
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
// 没有handler找到错误
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 找到handler的adapter,循环遍历adapter,判断adpater是否支持对应的handler。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 使用HandlerAdapter处理请求。普通的请求是RequestMappingHandlerAdapter。将请求转换为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);
}
}
}
}
普通的请求是RequestMappingHandlerAdapter。
![img_83789306d0cff24fd4711440f89552e7.png](https://i-blog.csdnimg.cn/blog_migrate/fb731ed4dbefa346bf2673a1b86efe27.png)
这一步涉及到DispatcherServlet的几大组件,其内部相对复杂,一下子不方便解释完整。现在能GET到的点
首先获取Handler
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
Handler处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
处理请求结果
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
当然除了DispatcherServelt的finally做了善后工作,内部很多也做了finally的善后。
回顾
梳理下这次分析源码所获得的知识,首先Servlet容器会调用init方法,而Spring MVC中init方法交给的是HttpServlet,HttpServletBean等做了处理。
在真正提高服务的方法上面,HttpServlet,FramworkServlet主要对请求做一些预处理,processRequest,然后交给DispatcherServelt的doSerice方法,doSerice又对分发做了一些准备,在request中做了一些设置,最后由DispatcherServelt做分发,真正的分发处理由Handler处理。
小结
Spring源码博大精深,短短一些话说不完,后面再持续更新Spring MVC的处理细节。
我想说细节太多,建议一起探索,可以相互交流奥
参考
Spring源码
TODO
- 在Servlet初始化之后,Spring框架对初始配置属性做的处理
- initWebApplicationContext的细节
- DispatcherServelt中的Handler处理细节