首先来看Servlet的相关接口
javax.servlet.Servlet
1 /** 2 * A servlet is a small Java program that runs within a Web server. 3 * Servlets receive and respond to requests from Web clients, 4 * usually across HTTP, the HyperText Transfer Protocol. 5 **/ 6 //javax.servlet.Servlet接口 7 public interface Servlet { 8 //servlet容器会保证servlet实例化后开始服务前调用init方法 9 public void init(ServletConfig config) throws ServletException; 10 11 public ServletConfig getServletConfig(); 12 13 public void service(ServletRequest req, ServletResponse res) 14 throws ServletException, IOException; 15 16 public String getServletInfo(); 17 //servlet容器关闭时被调用,tomcat的shutdown.bat 18 public void destroy(); 19 }
javax.servlet.http.HttpServlet
在使用原生servlet开发时,我们通常会继承javax.servlet.http.HttpServlet,包括SpringMVC中的DispatcherServlet也是继承了HttpServlet,并重写了doXxx方法。
1 //javax.servlet.http.HttpServlet 2 //HttpServlet实现了Servlet,重写的service方法只是做了类型强制转换,然后调用重载方法 3 public void service(ServletRequest req, ServletResponse res) 4 throws ServletException, IOException 5 { 6 HttpServletRequest request; 7 HttpServletResponse response; 8 9 if (!(req instanceof HttpServletRequest && 10 res instanceof HttpServletResponse)) { 11 throw new ServletException("non-HTTP request or response"); 12 } 13 14 request = (HttpServletRequest) req; 15 response = (HttpServletResponse) res; 16 17 service(request, response); 18 }
1 //service(ServletRequest req, ServletResponse res)的重载方法 2 protected void service(HttpServletRequest req, HttpServletResponse resp) 3 throws ServletException, IOException 4 { 5 String method = req.getMethod(); 6 7 if (method.equals(METHOD_GET)) { 8 doGet(req, resp); 9 } else if (method.equals(METHOD_HEAD)) { 10 doHead(req, resp); 11 } else if (method.equals(METHOD_POST)) { 12 doPost(req, resp); 13 } else if (method.equals(METHOD_PUT)) { 14 doPut(req, resp); 15 } else if (method.equals(METHOD_DELETE)) { 16 doDelete(req, resp); 17 } else if (method.equals(METHOD_OPTIONS)) { 18 doOptions(req,resp); 19 } else if (method.equals(METHOD_TRACE)) { 20 doTrace(req,resp); 21 } else { 22 String errMsg = lStrings.getString("http.method_not_implemented"); 23 Object[] errArgs = new Object[1]; 24 errArgs[0] = method; 25 errMsg = MessageFormat.format(errMsg, errArgs); 26 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); 27 } 28 }
以上简单回顾了Servlet了相关接口,下面就来看SpringMVC。
我们配置SpringMVC时都会在web.xml中配置一个Servlet
1 <servlet> 2 <servlet-name>springMVC</servlet-name> 3 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 4 <init-param> 5 <param-name>contextConfigLocation</param-name> 6 <param-value>classpath:spring-mvc.xml</param-value> 7 </init-param> 8 <load-on-startup>1</load-on-startup> 9 </servlet> 10 <servlet-mapping> 11 <servlet-name>springMVC</servlet-name> 12 <url-pattern>/</url-pattern> 13 </servlet-mapping>
这个Servlet就是SpringMVC的请求入口,将该Servlet的url-pattern配置为"/"表示默认Servlet,即没有其他Servlet匹配时,匹配该Servlet,这与"/*"不同,后者会匹配所有url,这就会出现问题。Servlet容器内部forward到jsp时仍然被映射到该Servlet,最终DispatcherServlet会调用Servlet容器的defaultServlet处理资源,即将jsp当做静态资源来处理了。还会出现性能问题,一般js、css等静态资源不会放在WEB-INF下,而是放在webapp下,当设置为"/*"时,静态仍然会走DispatcherServlet,然后再找到defaultServlet,这势必是低效的。
DispatcherServlet
DispatcherServlet的核心逻辑在doDispatch中
1 //DispatcherServlet的doDispatcher,有删减 2 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 3 4 // Determine handler for the current request. 5 //得到handler+interceptors 6 HandlerExecutionChain mappedHandler = getHandler(processedRequest); 7 8 // Determine handler adapter for the current request. 9 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 10 11 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 12 return; 13 } 14 15 // Actually invoke the handler. 16 ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 17 18 applyDefaultViewName(processedRequest, mv); 19 mappedHandler.applyPostHandle(processedRequest, response, mv); 20 //渲染 21 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 22 23 }
getHandler
1 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 2 for (HandlerMapping hm : this.handlerMappings) { 3 //HandlerExecutionChain中持有handler和interceptors 4 HandlerExecutionChain handler = hm.getHandler(request); 5 if (handler != null) { 6 return handler; 7 } 8 } 9 return null; 10 }
下面以以RequestMappingHandlerMapping为例来看hm.getHanlder
1 //RequestMappingHandlerMapping的getHandler的主要逻辑,有删减 2 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 3 4 Object handler = getHandlerInternal(request); 5 if (handler == null) { 6 handler = getDefaultHandler(); 7 } 8 //handler+interceptors 9 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); 10 11 return executionChain; 12 }
1 //AbstractHandlerMethodMapping的getHandlerInternal,有删减 2 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { 3 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); 4 5 List<Match> matches = new ArrayList<Match>(); 6 //根据url得到mappings,mapping即org.springframework.web.servlet.mvc.method.RequestMappingInfo,就是我们在@RequestMapping中设置的条件 7 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); 8 if (directPathMatches != null) { 9 addMatchingMappings(directPathMatches, matches, request); 10 } 11 12 13 if (!matches.isEmpty()) { 14 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); 15 //排序之后,首个元素就是最匹配的HandlerMethod 16 Collections.sort(matches, comparator); 17 18 Match bestMatch = matches.get(0); 19 20 handleMatch(bestMatch.mapping, lookupPath, request); 21 return bestMatch.handlerMethod; 22 } 23 else { 24 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); 25 } 26 }
1 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { 2 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? 3 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); 4 //用于matching 5 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); 6 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { 7 //<mvc:interceptor> 8 // <mvc:mapping path="/xxx/yyy" /> 9 //</mvc:interceptor> 10 if (interceptor instanceof MappedInterceptor) { 11 MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; 12 if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { 13 chain.addInterceptor(mappedInterceptor.getInterceptor()); 14 } 15 } 16 else { 17 chain.addInterceptor(interceptor); 18 } 19 } 20 return chain; 21 }
getHandlerAdapter
1 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 2 for (HandlerAdapter ha : this.handlerAdapters) { 3 if (ha.supports(handler)) { 4 return ha; 5 } 6 } 7 }
仍然以RequestMappingHandlerAdapter为例
1 //RequestMappingHandlerAdapter的supports 2 public final boolean supports(Object handler) { 3 //supportsInternal直接返回true 4 return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); 5 }
为什么要handlerAdapter呢?handler有很多种实现,比如@Controller、AbstractController等,每一种实现的handler方法都不尽相同,@Controller是完全自定义的方法,AbstractController重写handleRequestInternal()即可,DispatcherServlet要如何统一调用这么丰富的入口呢?DispatcherServlet面对的必须是一个接口,通过这个接口又可以调用到具体的handler,最简单的方法就是让handler实现该接口,但这样势必在handler中参入了“杂质”。现在我们面对的是一系列handler、一个DispatcherServlet、一个DispatcherServlet调用的接口,handler不容易直接实现该接口,而DispatcherServlet又一定要使用该接口,那就只能找一个适配器,将handler适配到接口上了,这就是HandlerAdapter,也即适配器模式。
applyPreHandle
1 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 HandlerInterceptor[] interceptors = getInterceptors(); 3 for (int i = 0; i < interceptors.length; i++) { 4 HandlerInterceptor interceptor = interceptors[i]; 5 if (!interceptor.preHandle(request, response, this.handler)) { 6 triggerAfterCompletion(request, response, null); 7 return false; 8 } 9 //注意这里会修改interceptorIndex,triggerAfterCompletion和postHandle都是从interceptorIndex开始向0遍历 10 this.interceptorIndex = i; 11 } 12 return true; 13 }
triggerAfterCompletion
1 void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) 2 throws Exception { 3 HandlerInterceptor[] interceptors = getInterceptors(); 4 //从interceptorIndex开始 5 for (int i = this.interceptorIndex; i >= 0; i--) { 6 HandlerInterceptor interceptor = interceptors[i]; 7 interceptor.afterCompletion(request, response, this.handler, ex); 8 } 9 }
applyPostHandle
1 void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { 2 HandlerInterceptor[] interceptors = getInterceptors(); 3 //从interceptorIndex开始 4 for (int i = interceptors.length - 1; i >= 0; i--) { 5 HandlerInterceptor interceptor = interceptors[i]; 6 interceptor.postHandle(request, response, this.handler, mv); 7 } 8 }
processDispatchResult
1 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 2 HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { 3 4 boolean errorView = false; 5 6 if (exception != null) { 7 //@ExceptionHandler方法 8 if (exception instanceof ModelAndViewDefiningException) { 9 logger.debug("ModelAndViewDefiningException encountered", exception); 10 mv = ((ModelAndViewDefiningException) exception).getModelAndView(); 11 } 12 //实现HandlerExceptionResolver 13 else { 14 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 15 mv = processHandlerException(request, response, handler, exception); 16 errorView = (mv != null); 17 } 18 } 19 //不论是Exception还是正常处理,统一进行渲染 20 // Did the handler return a view to render? 21 if (mv != null && !mv.wasCleared()) { 22 render(mv, request, response); 23 } 24 //之前当preHandle返回false时不执行postHanlde、handle,直接调用triggerAfterCompletion并返回 25 if (mappedHandler != null) { 26 mappedHandler.triggerAfterCompletion(request, response, null); 27 } 28 }
handle
1 //handle的核心方法,有删减 2 protected ModelAndView invokeHandlerMethod(HttpServletRequest request, 3 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { 4 5 ServletWebRequest webRequest = new ServletWebRequest(request, response); 6 7 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); 8 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); 9 10 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); 11 //HandlerMethodArgumentResolver解析参数(将形参赋值),它将Request中的数据解析到handler的参数上, 12 invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); 13 invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); 14 //databinder的作用是将Request中的parameters绑定到特定对象实例上,属于参数解析的过程 15 invocableMethod.setDataBinderFactory(binderFactory); 16 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); 17 18 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); 19 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); 20 /** 21 * Populate the model in the following order: 22 * <ol> 23 * <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}. 24 * <li>Invoke {@code @ModelAttribute} methods 25 * <li>Find {@code @ModelAttribute} method arguments also listed as 26 * {@code @SessionAttributes} and ensure they're present in the model raising 27 * an exception if necessary. 28 * </ol> 29 * @param request the current request 30 * @param container a container with the model to be initialized 31 * @param handlerMethod the method for which the model is initialized 32 * @throws Exception may arise from {@code @ModelAttribute} methods 33 */ 34 //initModel分3步初始化model: 35 //1从sessionAttributes获取@SessionAttributes中罗列的属性, 36 //2调用@ModelAttribute方法, 37 //3如果model中上不存在注有@ModelAttribute的形参的属性,则尝试从sessionAttributes中获取该属性 38 modelFactory.initModel(webRequest, mavContainer, invocableMethod); 39 //解析参数,然后反射调用函数 40 invocableMethod.invokeAndHandle(webRequest, mavContainer); 41 42 return getModelAndView(mavContainer, modelFactory, webRequest); 43 }
在反射调用真正handle之前,会遍历函数的形参,通过HandlerMethodArgumentResolver对形参进行解析,得到实参。比如注解为@PathVariable的形参,PathVariableMethodArgumentResolver通过分析url来得到实参;注解为@RequestBody的形参,RequestResponseBodyMethodProcessor通过调用messageConverter来解析http请求的body;普通的javabean,ModelAttributeMethodProcessor会通过dataBinder进行数据绑定。