SpringMVC 执行流程源码分析
用户发出请求到前端控制器 DispatcherServlet,DispatcherServlet收到请求后,调用 HandlerMapping(处理器映射器),HandlerMapping 找到具体的处理器(通过 XML 或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给 DispatcherServlet,然后 DispatcherServlet 调用 HandlerAdpter(处理器适配器),HandlerAdpter 经过适配调用具体的 Controller/Handler 的某个方法,Controller 执行完成返回 ModelAndView 对象。HandlerAdapter 将 Controller 返回的 ModelAndView 返回给 DispatcherServlet,然后再传递给 ViewReslover(视图解析器),最后解析后返回具体 View。DispatcherServlet 根据 View 进行渲染视图(将模型数据填充至视图中),响应用户。
之所以把单个映射器、适配器、视图解析器都分离开,是为了解耦。
源码分析 DispatcherServlet
DispatcherServlet 会拦截所有的前端请求,然后进行分发,继承了 HttpServlet。其父类 FrameworkServlet 重写了 service 方法。
RequestContextHolder 当一个请求被拦截后,进入了 service 方法中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @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); } } @Override protected final void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
发现,FrameworkServlet 覆盖了 doGet、doPOST 方法,所以是调用子类的 doGet、doPOST 方法。一般来说,框架设计中 使用 do 开头的方法是回调方法,或者是勾子方法,指的是触发某个事件时,会调用该方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 protected final void processRequest (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null ; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); 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); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null ) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); publishRequestHandledEvent(request, response, startTime, failureCause); } }
processRequest(request, response) 的具体实现可以分为以下几步:
获取上一个请求的参数
重新建立新的参数
设置新的参数到 XXContextHolder, Key 为当前的线程 值为请求对象
处理新的请求,如果出现异常,恢复 request
发布请求处理事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private void initContextHolders (HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {if (localeContext != null ) { LocaleContextHolder.setLocaleContext(localeContext, this .threadContextInheritable); } if (requestAttributes != null ) { RequestContextHolder.setRequestAttributes(requestAttributes, this .threadContextInheritable); } if (logger.isTraceEnabled()) { logger.trace("Bound request context to thread: " + request); } }
doService 中又调用了关键方法 doDispatch,看来分发步骤应该是在 doDispatch 方法中执行的。点进去看看:看注释分析
doDispatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 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 { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest); if (mappedHandler == null ) { noHandlerFound(processedRequest, response); return ; } HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 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 ; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return ; } 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) { 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()) { if (mappedHandler != null ) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
[1]getHandler(processedRequest) mappedHandler = getHandler(processedRequest);
方法中做的事情
1 2 3 4 5 6 7 8 9 10 11 protected HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { if (this .handlerMappings != null ) { for (HandlerMapping mapping : this .handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null ) { return handler; } } } return null ; }
我们自己配的 BeanNameUrlHandlerMapping
,按照 bean name 和 url 来映射 Handler。现在进入 getHandler(request)
看看做了什么,返回了一个 处理链。
getHandler(request) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public final HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null ) { handler = getDefaultHandler(); } if (handler == null ) { return null ; } if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this .corsConfigurationSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
可以看到 handler 对象包含以下属性。
根据后续代码的分析,发现 getHandler 方法最终返回的 HandlerExecutionChain 对象,本质上就是两个拦截器链和一个 handler。
HandlerExecutionChain 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class HandlerExecutionChain { private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class); private final Object handler; @Nullable private HandlerInterceptor[] interceptors; @Nullable private List<HandlerInterceptor> interceptorList; ... }
类中包含 handler 用于描述 映射方法的详细信息。
HandlerInterceptor[] 数组,拦截器链条,执行 handler 前,会按照该链条执行所有拦截器的 preHandle()
方法
List 链表,拦截器链条,执行 handler 前,会按照该链条执行所有拦截器的 preHandle()
方法
[2] getHandlerAdapter(Object handler) 1 2 3 4 5 6 7 8 9 10 11 protected HandlerAdapter getHandlerAdapter (Object handler) throws ServletException { if (this .handlerAdapters != null ) { for (HandlerAdapter adapter : this .handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler" ); }
遍历适配器,看适配器中是否有可以支持 handler 的执行。supports(handler)
方法实际上就是,看 handler 是否实现了 this.handlerAdapters
中对应的 Adapter 需要实现的接口。例如:SimpleControllerHandlerAdapter
需要 handler 实现 Controller
接口。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
返回的 ModelAndView,view 表示路径,这里只是逻辑路径,后续方法中会为其加入前缀和后缀,编程物理路径,model 中存放所有的共享数据
[3]processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 处理分发的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private void processDispatchResult (HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false ; if (exception != null ) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered" , exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null ); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null ); } } if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned." ); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return ; } if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(request, response, null ); } }
继续往下走
render(mv, request, response); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 protected void render (ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = (this .localeResolver != null ? this .localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null ) { view = resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view == null ) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'" ); } } else { view = mv.getView(); if (view == null ) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'" ); } } if (logger.isTraceEnabled()) { logger.trace("Rendering view [" + view + "] " ); } try { if (mv.getStatus() != null ) { response.setStatus(mv.getStatus().value()); } view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "]" , ex); } throw ex; } }
view.render(mv.getModelInternal(), request, response); 1 2 3 4 5 6 7 8 9 10 11 12 13 public void render (@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { logger.debug("View " + formatViewName() + ", model " + (model != null ? model : Collections.emptyMap()) + (this .staticAttributes.isEmpty() ? "" : ", static attributes " + this .staticAttributes)); } Map<String, Object> mergedModel = createMergedOutputModel(model, request, response); prepareResponse(request, response); renderMergedOutputModel(mergedModel, getRequestToExpose(request), response); }
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response); Output,可以看出,这一步就是将数据和视图输出了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 protected void renderMergedOutputModel ( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { exposeModelAsRequestAttributes(model, request); exposeHelpers(request); String dispatcherPath = prepareForRendering(request, response); RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath); if (rd == null ) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!" ); } if (useInclude(request, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including [" + getUrl() + "]" ); } rd.include(request, response); } else { if (logger.isDebugEnabled()) { logger.debug("Forwarding to [" + getUrl() + "]" ); } rd.forward(request, response); } }
至此,就完成了整个请求在 SpringMVC 中执行的整个过程。
手写 springMVC 框架
只编写一个 Servlet 去进行请求分发,后台只需要编写普通的处理器去处理不同的请求。
接收客户端发送过来的请求
根据请求URL查找对应的请求处理类
调用请求处理类的方法处理请求
处理响应结果
1 2 3 4 5 6 7 8 public class DispatcherServlet { public void doDispatch () { if ("/user" .equals(url)) { } } }
url-parttern 的配置方式:完整路径/xxx
,/
,/*
,*.后缀
/ 可以撇配除JSP之外的所有请求,在tomcat中web.xml文件规定 / 会交给 DefaultServlet 处理,还会匹配*.jsp交给 JspServlet 处理。
主流程
浏览器输入 /query URI 时,http://localhost/query?id=1&username=ammar
程序会进入我们在 web.xml 中配置的 servlet
web.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns ="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation ="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version ="2.5" > <servlet > <servlet-name > DispatcherServlet</servlet-name > <servlet-class > cn.lizhaoloveit.springmvc.servlet.DispatchServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc.xml</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > DispatcherServlet</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
DispatcherServlet
此时,使用了抽象模板的设计模式来构建 DispatcherServlet
,创建抽象类 AbstractHttpServlet
继承 HttpServlet
1 2 3 4 5 6 7 8 9 10 11 public abstract class AbstractHttpServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doDispatch(req, resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } public abstract void doDispatch (HttpServletRequest request, HttpServletResponse response) ; }
使用钩子方法 doDispatch
处理所有的请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class DispatchServlet extends AbstractHttpServlet { private List<HandlerMapping> handlerMappings = new ArrayList<>(); private List<HandlerAdapter> handlerAdapters = new ArrayList<>(); @Override public void init (ServletConfig config) { String contextConfigLocation = config.getInitParameter("contextConfigLocation" ); BeanFactory beanFactory = new DefaultListableBeanFactory(contextConfigLocation); beanFactory.getBeanNamesByType(Object.class); handlerMappings = beanFactory.getBeansByType(HandlerMapping.class); handlerAdapters = beanFactory.getBeansByType(HandlerAdapter.class); } @Override public void doDispatch (HttpServletRequest request, HttpServletResponse response) { Object handler = null ; for (HandlerMapping handlerMapping : handlerMappings) { Object handlerTemp = handlerMapping.getHandler(request); if (handlerTemp != null ) handler = handlerTemp; } HandlerAdapter ha = null ; for (HandlerAdapter handlerAdapter : handlerAdapters) { if (handlerAdapter.support(handler)) { ha = handlerAdapter; } } try { ha.handleRequest(handler, request, response); } catch (Exception e) { e.printStackTrace(); } } }
此时,由于处理请求的处理器多种多样,无法用一个接口来概括所有的处理方式,所以采用了策略设计模式,用我们预先写好的 request 和 handler 的映射器 HandlerRequestMapping 去匹配请求,如果可以找到映射。就可以找到处理器。
HandlerMapping 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class RequestMappingHandlerMapping implements HandlerMapping , BeanFactoryAware { private Map<String, HandlerMethod> urlHandlerMethodMap = new HashMap<>(); private BeanFactory beanFactory; public void setBeanFactory (BeanFactory beanFactory) { this .beanFactory = beanFactory; } public void initMethod () { List<String> beanNames = beanFactory.getBeanNamesByType(Object.class); for (String beanName : beanNames) { Map<String, BeanDefinition> beanDefinitions = beanFactory.getBeanDefiitions(); BeanDefinition beanDefinition = beanDefinitions.get(beanName); String beanClassName = beanDefinition.getBeanClassName(); try { Class<?> clazz = Class.forName(beanClassName); if (clazz.isAnnotationPresent(Controller.class)) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(RequestMapping.class)) { RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); String url = requestMapping.value(); HandlerMethod handlerMethod = new HandlerMethod(method, beanFactory.getBean(beanName)); urlHandlerMethodMap.put(url, handlerMethod); } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } @Override public Object getHandler (HttpServletRequest request) { String uri = request.getRequestURI(); return urlHandlerMethodMap.get(uri); } }
HandlerMapping 类在初始化时就已经将项目中所有的映射关系保存在 Map 中了,在拿到请求后,只需要判断 Map 中是否有 url key。
HandlerAdapter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class RequestMappingHandlerAdapter implements HandlerAdapter { @Override public boolean support (Object handler) { return handler instanceof HandlerMethod; } @Override public void handleRequest (Object handler, HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); Object object = handlerMethod.getHandler(); Map<String, String[]> paramMap = request.getParameterMap(); List<Object> args = new ArrayList<>(); Parameter[] parameters = method.getParameters(); for (Parameter parameter : parameters) { String name = parameter.getName(); Class<?> type = parameter.getType(); String[] value = paramMap.get(name); if (value == null ) { args.add(null ); continue ; } if (type == Integer.class) { args.add(Integer.valueOf(value[0 ])); } else if (type == String.class) { args.add(value[0 ]); } } Object[] argsObj = args.toArray(); Object returnValue = method.invoke(object, argsObj); if (method.isAnnotationPresent(ResponseBody.class)) { if (returnValue instanceof String) { response.setContentType("text/plain;charset=utf8" ); response.getWriter().write(returnValue.toString()); } else if (returnValue instanceof Map) { response.setContentType("application/json;charset=utf8" ); String s = JsonUtils.object2Json(returnValue); response.getWriter().write(s); } } else { } } }
Adapter 是 springmvc 框架中处理器输入输出的中转站。将数据按规则过滤和处理。
最终的效果:
url: http://localhost/query?id=3&username=ammar 浏览器返回
1 2 3 4 { "id" : 3 , "username" : "ammar" }
代码地址:手写springmvc框架