流程梳理
dispatcherServlet作为前端控制器的主要作用就是接受请求与处理响应。
不过它不是传统意义上的servlet,它在接受到请求后采用转发的方式,将具体工作交给专业人士去做。
参与角色主要有:
处理映射器(HandlerMapping)
处理器((Handler)Controller)
视图(View)
(找了一张图,把请求过程与步骤清晰的呈现了出来)
第一步:前端控制器dispatcher接受请求
第二步:前端控制器去发起handler映射查找请求
第三步:处理器映射器查找hanlder并返回HandlerExetuionChain
第四步:前端控制器发起请求处理器适配器请求执行
第五步:处理器适配器去调用handler执行
第六步:处理器处理后返回ModelAndView给HandlerAdapter
第七步:处理器适配器将ModelAndView返回给前端控制器
第八步:前端控制器请求视图解析器解析ModelAndView
第九步:视图解析器解析视图后返回视图View给前端控制器
第十步:前端控制器请求视图要求渲染视图
第十一步:前端控制器返回响应
源码探秘
第一步接受请求:
我们可以来看看DispatcherServlet的继承结构
其实DispatcherServlet能处理请求是因为HttpServlet类的service方法,而HttpServlet又来自Servlet接口定义的规范。
可以看到抽象类HttpServlet实现了接口Servlet的service方法,根据请求类型不同执行了不同的方法(doGet,doPost)
当请进来后,由HttpServlet的子类FrameworkServlet重写的service方法执行请求,可以看到437行子类调用了父类的service方法,然后在父类执行doGet之类的方法时,由于子类FrameworkServlet重写了父类方法,交由子类执行,所以进到了我的doGet断点里面,它调用了处理请求方法。
接下来我们看看ProcessRequest方法的源码
1 protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
2 long startTime = System.currentTimeMillis();
3 Throwable failureCause = null;
4 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
5 LocaleContext localeContext = this.buildLocaleContext(request);
6 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
7 ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
8 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
9 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
10 this.initContextHolders(request, localeContext, requestAttributes);
11
12 try {
13 this.doService(request, response);
14 } catch (IOException | ServletException var16) {
15 failureCause = var16;
16 throw var16;
17 } catch (Throwable var17) {
18 failureCause = var17;
19 throw new NestedServletException("Request processing failed", var17);
20 } finally {
21 this.resetContextHolders(request, previousLocaleContext, previousAttributes);
22 if (requestAttributes != null) {
23 requestAttributes.requestCompleted();
24 }
25
26 this.logResult(request, response, (Throwable)failureCause, asyncManager);
27 this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
28 }
29
30 }
前面一系列初始化工作我们先不管,看看重要的部分,try里面的doService方法
跟踪进去看了一下,由于它是抽象方法,所以会由子类实现和执行,也就是我们的DispatchServlet类了
1protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
2 this.logRequest(request);
3 MapString, Object attributesSnapshot = null;
4 if (WebUtils.isIncludeRequest(request)) {
5 attributesSnapshot = new HashMap();
6 Enumeration attrNames = request.getAttributeNames();
7
8 label95:
9 while(true) {
10 String attrName;
11 do {
12 if (!attrNames.hasMoreElements()) {
13 break label95;
14 }
15
16 attrName = (String)attrNames.nextElement();
17 } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
18
19 attributesSnapshot.put(attrName, request.getAttribute(attrName));
20 }
21 }
22
23 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
24 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
25 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
26 request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
27 if (this.flashMapManager != null) {
28 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
29 if (inputFlashMap != null) {
30 request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
31 }
32
33 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
34 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
35 }
36
37 try {
38 this.doDispatch(request, response);
39 } finally {
40 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
41 this.restoreAttributesAfterInclude(request, attributesSnapshot);
42 }
43
44 }
45
46 }
所以第一步也就完成了,第一步的任务就是走进这里来。
第二步:前端控制器去发起handler映射查找请求
Dispatcher—HttpServletRequest— HandlerMapping
上面的源码中主要工作就是给request实例设置一系列参数,要注意的就是doDispatch方法,这里面就是mvc的核心了,前面第一张交互图里面的流程都是在这里实现的。
可以看到,通过HttpRequestServlet作为参数请求handlerMapping
第三步:处理器映射器查找hanlder并返回HandlerExetuionChain
Dispatcher —HandlerExeutionChain—HandlerMapping
可以看到上图中返回了mappedHandler变量,就是HandlerExtuceChain类型
可以看到,已经找到并返回了我们的HomeController处理器(Hanlder)
第四步:前端控制器发起请求处理器适配器请求执行
Dispatcher—Handler— HandlerAdapter
从508行可以看到,适配器传入handler对象和reaquest等信息,执行handler()方法
第五步:处理器适配器去调用handler执行
HandlerAdapter—HttpServletRequest Handler(Controller)
从这里可以看到,调用的handlerInternal是个抽象方法,会调用子类的实现方法,子类由RequestMappingHandlerAdapter实现,这个类也是我们经常在xml里面配置的类
方法执行后返回我们的index
第六步:处理器处理后返回ModelAndView给HandlerAdapter
HandlerAdapter —ModelAndView—Handler(Controller)
第七步:处理器适配器将ModelAndView返回给前端控制器
Dispatcher —ModelAndView—HandlerAdapter
第八步:前端控制器请求视图解析器解析ModelAndView
Dispatcher—ModelAndView— ViewReslover
第九步:视图解析器解析视图后返回视图View给前端控制器
Dispatcher —View—ViewReslover
可以看到,返回的视图,url指向index.jsp页面
第十步:前端控制器请求视图要求渲染视图
Dispatcher—View—render
如果View对象不为空,将会调用render方法渲染
如果返回的是json对象,属于接口的,是不会走这里的
此时会找对应的视图解析器去渲染
里面其实也没干啥,就做了个跳转,到jsp页面去绑定数据
第十一步:前端控制器返回响应
Response —Dispatcher
到这里也就基本上完了。
处理请求完成后做了个重置工作,然后发布一个事件,你可以选择监听这个事件,做相应处理。
再看看response里面
这个就是我们页面上的内容了。
原文始发于微信公众号(JAVA烂猪皮):