SpringMVC 执行过程分析
1. SpringMVC 大致过程测试
1. 新建filter
package cn.xm.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter(filterName = "testFilter", urlPatterns = "/*") public class TestFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { System.out.println("cn.xm.filter.TestFilter start"); chain.doFilter(req, res); System.out.println("cn.xm.filter.TestFilter end"); } @Override public void destroy() { } @Override public void init(FilterConfig arg0) throws ServletException { } }
2. 新建一个SpringMVC拦截器
package cn.xm.inteceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInteceptor implements HandlerInterceptor { /** * 在请求处理之前进行调用(Controller方法调用之前) */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("被 MyInterceptor1 preHandle拦截,放行..."); return true; } /** * 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后) */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("被 MyInterceptor1 postHandle,放行..."); } /** * 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行 (主要是用于进行资源清理工作) */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("被 MyInterceptor1 afterCompletion 拦截,放行..."); } }
3. 配置类,加入到Spring的拦截器链中
package cn.xm.config; import cn.xm.inteceptor.MyInteceptor; import cn.xm.inteceptor.MyInteceptor2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import javax.annotation.PostConstruct; /** * 1.JSON返回实体类报错2.设置页面的默认页面 * * @author Administrator */ @Configuration public class MVCConfig extends WebMvcConfigurerAdapter { @Autowired private ServletRegistrationBean servletRegistrationBean; @PostConstruct public void postConstruct() { servletRegistrationBean.getUrlMappings().clear(); servletRegistrationBean.getUrlMappings().add("*.do"); System.out.println("cn.xm.config.MVCConfig.postConstruct======"); } /** * 解决JSON返回实体类报错 */ @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.favorPathExtension(false); } /** * 设置页面的默认页面 */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("forward:/index.html"); registry.setOrder(Ordered.HIGHEST_PRECEDENCE); super.addViewControllers(registry); } @Override public void addInterceptors(InterceptorRegistry registry) { /** * 拦截器按照顺序执行, 不写addPathPatterns 默认匹配所有 */ registry.addInterceptor(new MyInteceptor()).addPathPatterns("/test2/**"); super.addInterceptors(registry); } }
4. 新建一个Controller
package cn.xm.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @RequestMapping("test2") @Controller("testController2") public class TestController { @GetMapping("test1") public String test1() { System.out.println("cn.xm.controller.TestController.test1 方法调用"); return "test1"; } @GetMapping("test2") @ResponseBody public String test2() { System.out.println("cn.xm.controller.TestController.test2 方法调用"); return "test2"; } }
5. curl test2 方法进行测试
$ curl http://localhost:8088/test2/test2 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 5 100 5 0 0 36 0 --:--:-- --:--:-- --:--:-- 37test2
5. 查看控制台日志如下:
cn.xm.filter.TestFilter start
被 MyInterceptor1 preHandle拦截,放行...
cn.xm.controller.TestController.test2 方法调用
被 MyInterceptor1 postHandle,放行...
被 MyInterceptor1 afterCompletion 拦截,放行...
cn.xm.filter.TestFilter end
6. 过程分析:
其执行过程大致可以分为下面过程:
-filter链chain.doFilter(req, res); 之前
-inteceptor preHandle
-controller
-inteceptor postHandle
-inteceptor afterCompletion
-filter链chain.doFilter(req, res); 之后
2. Spring MVC DispatcherServlet配置分析
1. 创建DispatcherServlet (DispatcherServletConfiguration 配置类是创建Servlet)
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletConfiguration#dispatcherServlet
@Bean( name = {"dispatcherServlet"} ) public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails()); return dispatcherServlet; }
2. DispatcherServletRegistrationConfiguration 配置DispatcherServlet的注册类-Servlet实例是要被添加(注册)到如tomcat这样的ServletContext里的,这样才能够提供请求服务。所以,DispatcherServletRegistrationConfiguration将生成一个Bean,负责将DispatcherServlet给注册到ServletContext中。
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration#dispatcherServletRegistration
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServletRegistrationBean dispatcherServletRegistration( DispatcherServlet dispatcherServlet) { DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean( dispatcherServlet, this.serverProperties.getServlet().getPath()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup( this.webMvcProperties.getServlet().getLoadOnStartup()); if (this.multipartConfig != null) { registration.setMultipartConfig(this.multipartConfig); } return registration; }
默认的path是/ , 会匹配所有请求
/** * Path of the main dispatcher servlet. */ private String path = "/";
3. 修改请求路径:
(1) 修改允许根据后缀请求
@Configuration public class UrlMatchConfig extends WebMvcConfigurationSupport { @Override public void configurePathMatch(PathMatchConfigurer configurer) { // setUseSuffixPatternMatch 后缀模式匹配 configurer.setUseSuffixPatternMatch(true); // setUseTrailingSlashMatch 自动后缀路径模式匹配 configurer.setUseTrailingSlashMatch(true); } }
(2) 修改允许的请求路径
/** * 设置匹配*.action后缀请求 * * @param dispatcherServlet * @return */ @Bean public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) { ServletRegistrationBean servletServletRegistrationBean = new ServletRegistrationBean(dispatcherServlet); // 参数接受可变类型的多个参数支持多种后缀的匹配 servletServletRegistrationBean.addUrlMappings("*.action", "*.do"); return servletServletRegistrationBean; }
3. SpringMVC DispatcherServlet 执行过程分析
DispatcherServlet 也叫中央处理器,是处理请求中比较核心的一个类。
1. 其doService方法如下:
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("org.springframework.web.servlet")) { 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); } } } }
2. 前面做了一堆处理之后将处理转交给doDispatch方法
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); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. 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. 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); } } } }
接下来以这个方法为入口进行研究
1》 请求经过一系列filter验证之后到达DispatcherServlet
2》 先调用org.springframework.web.servlet.DispatcherServlet#getHandler 获取到一个HandlerExecutionChain, 方法如下:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
org.springframework.web.servlet.HandlerExecutionChain 核心属性如下:
private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1;
this.handlerMappings 包含如下对象:
2.1》到index为2 的org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 获取到满足条件的HandlerExecutionChain ,其方法继承自父类org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
(1)getHandlerInternal(request); 获取 handler ;也就是是根据请求的URI获取到的Controller的信息,如下:
(2) 然后根据获取到的handler去获取对应的inteceptor信息和request 去获取inteceptors 拦截器,org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
this.adaptedInterceptors 如下: 也就是我们自己的拦截器和Spring默认提供的几个拦截器。 获取到之后会调用下面的mappedInterceptor.matches(lookupPath, this.pathMatcher) 方法来验证是否该请求是否满足拦截器拦截的路径
注意,这里看到我们自己的MyInteceptor变为了MappedInteceptor,其改变是在:org.springframework.web.servlet.config.annotation.InterceptorRegistration#getInterceptor
protected Object getInterceptor() { if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) { return this.interceptor; } String[] include = toArray(this.includePatterns); String[] exclude = toArray(this.excludePatterns); MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor); if (this.pathMatcher != null) { mappedInterceptor.setPathMatcher(this.pathMatcher); } return mappedInterceptor; }
可以看到是根据是否有patterns决定是否变为MapperInteceptor ,是MappedInteceptor在后续需要根据Uri进行match,否则匹配所有的请求,所以在注入的时候会有两种情况,如下:
@Configuration public class MVCConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { /** * 拦截器按照顺序执行, 不写addPathPatterns 默认匹配所有 */ // 如果有addPathPatterns\excludePathPatterns 会用MappedInteceptor进行包裹 registry.addInterceptor(new MyInteceptor()).addPathPatterns("/test2/**").excludePathPatterns(""); // 没有pattern,直接匹配所有的请求,会直接放到拦截器链chain中 registry.addInterceptor(new MyInteceptor2()); super.addInterceptors(registry); } }
(3)返回去给DispatcherServlet的 mappedHandler 如下:
3》 然后根据handler获取到对应的handlerAdapter对象
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
最后调supports方法(),如下:org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports
public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
最后返回给Dispatcher的HandlerAdapter如下:
4》 接下来调用org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle 处理Inteceptor的前置请求(这里需要注意,如果前置请求有返回false则直接请求结束)
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
inteceprors 如下:
然后正向遍历这个数组,依次调用其preHandle 方法,如果全部返回true则返回下一步;如果有某个返回false,则调用org.springframework.web.servlet.HandlerExecutionChain#triggerAfterCompletion 方法:(可以看到使用的下标是interceptorIndex,这个是在上面方法调用成功的次数。 也就是preHandle 返回true的Inteceptor,然后逆序执行其interceptorIndexafterCompletion方法)
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } }
5》 调用RequestMappingHandlerAdapter#handle 进行处理, 是继承自父类org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle:
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); } protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
(1)核心在handleInternal 方法,方法内部先检查request 是否合法:
protected final void checkRequest(HttpServletRequest request) throws ServletException { // Check whether we should support the request method. String method = request.getMethod(); if (this.supportedMethods != null && !this.supportedMethods.contains(method)) { throw new HttpRequestMethodNotSupportedException( method, StringUtils.toStringArray(this.supportedMethods)); } // Check whether a session is required. if (this.requireSession && request.getSession(false) == null) { throw new HttpSessionRequiredException("Pre-existing session required but none found"); } }
(2) 然后调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod 反射调用我们Controller的方法:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) { logger.debug("Found concurrent result value [" + result + "]"); } invocableMethod = invocableMethod.wrapConcurrentResult(result); } invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
(2.1) invocableMethod.invokeAndHandle(webRequest, mavContainer); 里面反射调用方法===这个方法反射调用方法,然后根据是否是ResponseBody进行处理,如果是ResponseBody会在这个处理直接将结果写到客户端
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle 如下:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(this.responseReason)) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } throw ex; } }
org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest 方法解析到参数之后反射进行方法调用,解析参数会交给对应的参数解析器进行解析(如果是@RequestBody 会交给RequestResponseBodyMethodProcessor 处理)
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "' with arguments " + Arrays.toString(args)); } Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "] returned [" + returnValue + "]"); } return returnValue; }
doInvoke 会反射调用方法,然后获取到方法的返回值。
(2.2) 接下来处理回传结果,对应的处理代码:this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
获取到的handler如下:
然后调用org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue 方法
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); // Try even with null return value. ResponseBodyAdvice could get involved. writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }
然后调用org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters 里面用一坨代码判断,如果是String类型直接用 text/plain 写回去,如果是其他类型则转JSON写回去。
所以可以看到如果是ResponseBody的请求,那么invocableMethod.invokeAndHandle(webRequest, mavContainer); 方法走完客户端就可以收到数据。
(3) getModelAndView(mavContainer, modelFactory, webRequest); 返回ModelAndView 对象给DispatcherServlet。 这里需要注意,如果是ResponseBody返回的JSON数据,那么返回的ModelAndView对象是null,否则会返回一个带视图名称的对象,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getModelAndView:
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); // ResponseBody声明的在前面已经处理过,直接返回null if (mavContainer.isRequestHandled()) { return null; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; }
比如/test2/test1 请求返回的ModelAndView对象如下:
6》接下来调用mappedHandler.applyPostHandle(processedRequest, response, mv); 方法处理拦截器的postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv): 可以看到是和前置相反的顺序执行,也就是preHandle顺序是(inteceptor1-》inteceptor2-》inteceptor3), postHandle是 从3-》1 这样的顺序执行
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } }
7》接下来processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 方法处理ModelAndView结果,如下:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, 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); } } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }
7.1》如果是回传JSON数据,ModelAndView是null,则不会走render方法,否则会走render方法:
org.springframework.web.servlet.DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. Locale locale = this.localeResolver.resolveLocale(request); response.setLocale(locale); View view; if (mv.isReference()) { // We need to resolve the view name. view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { // No need to lookup: the ModelAndView object contains the actual View object. 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() + "'"); } } // Delegate to the View object for rendering. if (logger.isDebugEnabled()) { logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'"); } 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 + "] in DispatcherServlet with name '" + getServletName() + "'", ex); } throw ex; } }
org.springframework.web.servlet.DispatcherServlet#resolveViewName 解析获取view 视图信息:
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } return null; }
比如: test2/test1 接口返回的view信息如下:
接下来调用 view.render(mv.getModelInternal(), request, response); 渲染视图, 这个里面就是找对应的thymeleaf 模板然后渲染数据,如果渲染不到进行报错。
视图渲染完成之后会调用 mappedHandler.triggerAfterCompletion(request, response, null); 方法调用拦截器链的afterCompletion 方法(也是逆向进行)
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } }
处理完成则doDispatch方法结束,继续执行过滤器后半部分代码。
总结:
1. 整个DispatcherServlet 处理过程可以用下图表示:
2. 根据请求的uri获取HandlerExecutionChaindler 是从一个维护URL与地址信息的map中获取的
RequestMappingHandlerMapping获取HandlerExecutionChaindler的方法getHandler是继承自父类的方法:org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler,内部获取处理器HandlerMethod的方法是:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); if (logger.isDebugEnabled()) { if (handlerMethod != null) { logger.debug("Returning handler method [" + handlerMethod + "]"); } else { logger.debug("Did not find handler method for [" + lookupPath + "]"); } } return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
然后lookupHandlerMethod 是org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } if (!matches.isEmpty()) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); Collections.sort(matches, comparator); if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } Match bestMatch = matches.get(0); if (matches.size() > 1) { if (CorsUtils.isPreFlightRequest(request)) { return PREFLIGHT_AMBIGUOUS_MATCH; } Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); } } handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
(1)List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); 获取到请求的路径和方法,org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl是从自己的属性里面获取, urlLookup 是一个维护了URL路径信息的map
(2) 然后加到上面的matches 里面
(3) 然后获取到集合第一个元素之后返回其handlerMethod对象(包装了在Spring容器中的beanName以及其方法相关信息), 用于之后反射调用方法进行处理
补充:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping 注入Spring中
public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping handlerMapping = createRequestMappingHandlerMapping(); handlerMapping.setOrder(0); handlerMapping.setInterceptors(getInterceptors()); handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager()); handlerMapping.setCorsConfigurations(getCorsConfigurations()); PathMatchConfigurer configurer = getPathMatchConfigurer(); if (configurer.isUseSuffixPatternMatch() != null) { handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch()); } if (configurer.isUseRegisteredSuffixPatternMatch() != null) { handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch()); } if (configurer.isUseTrailingSlashMatch() != null) { handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch()); } UrlPathHelper pathHelper = configurer.getUrlPathHelper(); if (pathHelper != null) { handlerMapping.setUrlPathHelper(pathHelper); } PathMatcher pathMatcher = configurer.getPathMatcher(); if (pathMatcher != null) { handlerMapping.setPathMatcher(pathMatcher); } return handlerMapping; }
设置了一系列的参数,其中包括获取拦截器getInterceptors(),如下:
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getInterceptors
protected final Object[] getInterceptors() { if (this.interceptors == null) { InterceptorRegistry registry = new InterceptorRegistry(); addInterceptors(registry); registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); this.interceptors = registry.getInterceptors(); } return this.interceptors.toArray(); }
补充: 根据请求获取request的规则
有时候一个请求会匹配到多个满足条件的handler,比如:
GET /v1/v2
PUT /v1/v2
GET /v1/{path}
PUT /v1/{path}
1. 这时候访问/v1/v2 的时候会匹配到两个路径,SpringMVC 处理逻辑是先根据全路径去查找,然后后根据方法再次过滤
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } if (!matches.isEmpty()) { Match bestMatch = matches.get(0); if (matches.size() > 1) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); bestMatch = matches.get(0); if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } if (CorsUtils.isPreFlightRequest(request)) { return PREFLIGHT_AMBIGUOUS_MATCH; } Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); String uri = request.getRequestURI(); throw new IllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
(1) org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl 根据全路径找
@Nullable public List<T> getMappingsByUrl(String urlPath) { return this.urlLookup.get(urlPath); }
比如我们访问GET /v1/v2 问找到两个RequestMappingInfo
接下来拿找到的两个到addMatchingMappings 进行筛选:
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { for (T mapping : mappings) { T match = getMatchingMapping(mapping, request); if (match != null) { matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } }
接下来调用:org.springframework.web.servlet.mvc.method.RequestMappingInfo#getMatchingCondition
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) { RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request); if (methods == null) { return null; } ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request); if (params == null) { return null; } HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request); if (headers == null) { return null; } ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request); if (consumes == null) { return null; } ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request); if (produces == null) { return null; } PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); if (patterns == null) { return null; } RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); if (custom == null) { return null; } return new RequestMappingInfo(this.name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); }
然后这里面就是根据对应的方法、参数、头、consumes(可以接收的消息类型)、produces(返回去的消息类型)等条件进行判断。 会筛选到index为1的RequestMappingInfo。
然后到下面找到最佳匹配的bestMatch, 然后获取handleMethod。
(2) 如果我们获取GET /v1/v3。
首先根据全路径找找不到匹配的,
然后获取到系统所有的包括变量型路径:
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappings:mappingLookup 实际是容器启动时扫描到的保存的所有信息,T类型是RequestMappingInfo
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); public Map<T, HandlerMethod> getMappings() { return this.mappingLookup; }
然后遍历系统所有的RequestMappingInfo,调用方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#addMatchingMappings 遍历所有的RequestMappingInfo进行匹配,并且匹配到不会停止,会返回一个满足条件的集合。
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { for (T mapping : mappings) { T match = getMatchingMapping(mapping, request); if (match != null) { matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } }
找到之后返回去,如果数量大于1,做排序比较后拿到最优解。只有一个那直接返回其handlerMethod。
(3) 如果这一步没找到handler,则走后面的org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleNoMatch 进行抛异常会其他处理,这里可以覆盖。
@Override protected HandlerMethod handleNoMatch( Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException { PartialMatchHelper helper = new PartialMatchHelper(infos, request); if (helper.isEmpty()) { return null; } if (helper.hasMethodsMismatch()) { Set<String> methods = helper.getAllowedMethods(); if (HttpMethod.OPTIONS.matches(request.getMethod())) { HttpOptionsHandler handler = new HttpOptionsHandler(methods); return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD); } throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods); } if (helper.hasConsumesMismatch()) { Set<MediaType> mediaTypes = helper.getConsumableMediaTypes(); MediaType contentType = null; if (StringUtils.hasLength(request.getContentType())) { try { contentType = MediaType.parseMediaType(request.getContentType()); } catch (InvalidMediaTypeException ex) { throw new HttpMediaTypeNotSupportedException(ex.getMessage()); } } throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes)); } if (helper.hasProducesMismatch()) { Set<MediaType> mediaTypes = helper.getProducibleMediaTypes(); throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes)); } if (helper.hasParamsMismatch()) { List<String[]> conditions = helper.getParamConditions(); throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap()); } return null; }
总结:
1. MappingRegistry 有两个重要的缓存map
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
mappingLookup 存放系统中所有的RequestMappingInfo和HandlerMethod 映射关系。RequestMappingInfo是封装我们方法上的@RequestMapping 注解的类,handlerMethod 则是记录我们方法的信息。(这里存放所有的,包括带变量的和不带变量的)
urlLookup 是存放URI与RequestMappingInfo 关系,MultiValueMap 是内部是一个集合,一个key对一个集合。(这里只存全路径,对于路径带变量的不会存到这里)
2. 请求获取handlerMethod 如下:
(1)一个请求进来先根据URI到urlLookup 获取RequestMappingInfo (只能获取全路径的,带变量的获取不到)
》获取到就拿获取的调用方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#addMatchingMappings 判断是否满足条件,满足加到matches集合中
(2) 如果走完上面第一步matches为空,就拿mappingLookup 的Key集合,也就是系统所有的RequestMappingInfo 去遍历找是否有满足的(会处理带变量的匹配)
(3)如果上面第二步返回的matches不会空,处理matches 集合,拿到最佳匹配, 其比较器如下:org.springframework.web.servlet.mvc.method.RequestMappingInfo#compareTo
public int compareTo(RequestMappingInfo other, HttpServletRequest request) { int result; // Automatic vs explicit HTTP HEAD mapping if (HttpMethod.HEAD.matches(request.getMethod())) { result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); if (result != 0) { return result; } } result = this.patternsCondition.compareTo(other.getPatternsCondition(), request); if (result != 0) { return result; } result = this.paramsCondition.compareTo(other.getParamsCondition(), request); if (result != 0) { return result; } result = this.headersCondition.compareTo(other.getHeadersCondition(), request); if (result != 0) { return result; } result = this.consumesCondition.compareTo(other.getConsumesCondition(), request); if (result != 0) { return result; } result = this.producesCondition.compareTo(other.getProducesCondition(), request); if (result != 0) { return result; } // Implicit (no method) vs explicit HTTP method mappings result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); if (result != 0) { return result; } result = this.customConditionHolder.compareTo(other.customConditionHolder, request); if (result != 0) { return result; } return 0; }
(4) 如果获取到的matches为空,则走org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#handleNoMatch 方法。
补充: SpringMVC 注册Handler 过程
1. org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport 这个类里面注入了 RequestMappingHandlerMapping。这个配置类是被EnableWebMvc 注解引入的。
2. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet 实现的InitializingBean 的方法,这个方法是在bean 初始化过程中会被调用
public void afterPropertiesSet() { this.config = new BuilderConfiguration(); this.config.setUrlPathHelper(this.getUrlPathHelper()); this.config.setPathMatcher(this.getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch()); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch()); this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch()); this.config.setContentNegotiationManager(this.getContentNegotiationManager()); super.afterPropertiesSet(); }
然后调用其父类方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
public void afterPropertiesSet() { this.initHandlerMethods(); }
initHandlerMethods() 方法如下:
protected void initHandlerMethods() { String[] var1 = this.getCandidateBeanNames(); int var2 = var1.length; for(int var3 = 0; var3 < var2; ++var3) { String beanName = var1[var3]; if (!beanName.startsWith("scopedTarget.")) { this.processCandidateBean(beanName); } } this.handlerMethodsInitialized(this.getHandlerMethods()); }
主要逻辑是:根据容器中的对象,遍历对象获取其class,判断是否有@Controller 注解
this.processCandidateBean(beanName);
protected void processCandidateBean(String beanName) { Class beanType = null; try { beanType = this.obtainApplicationContext().getType(beanName); } catch (Throwable var4) { if (this.logger.isTraceEnabled()) { this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4); } } if (beanType != null && this.isHandler(beanType)) { this.detectHandlerMethods(beanName); } }
1》 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler 判断是否是Handler的方法。
protected boolean isHandler(Class<?> beanType) { return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class); }
2》 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods 解析handler里面的方法
protected void detectHandlerMethods(Object handler) { Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass(); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> { try { return this.getMappingForMethod(method, userType); } catch (Throwable var4) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4); } }); if (this.logger.isTraceEnabled()) { this.logger.trace(this.formatMappings(userType, methods)); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); this.registerHandlerMethod(handler, invocableMethod, mapping); }); } }
(1) org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod 解析获取RequestMappingInfo 对象
@Nullable protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = this.createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } String prefix = this.getPathPrefix(handlerType); if (prefix != null) { info = RequestMappingInfo.paths(new String[]{prefix}).options(this.config).build().combine(info); } } return info; }
创建方法如下:
@Nullable private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = element instanceof Class ? this.getCustomTypeCondition((Class)element) : this.getCustomMethodCondition((Method)element); return requestMapping != null ? this.createRequestMappingInfo(requestMapping, condition) : null; }
如果方法带了RequestMapping 注解会创建RequestMappingInfo 对象然后返回。 继续调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(org.springframework.web.bind.annotation.RequestMapping, org.springframework.web.servlet.mvc.condition.RequestCondition<?>):
protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { Builder builder = RequestMappingInfo.paths(this.resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes()).produces(requestMapping.produces()).mappingName(requestMapping.name()); if (customCondition != null) { builder.customCondition(customCondition); } return builder.options(this.config).build(); }
创建对象时获取了方法上的一些注解信息(包括头、consumes(可以接受的消息类型)、produces 可以生产的消息类型)
3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#registerHandlerMethod调用这个注册mapping 信息
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { super.registerHandlerMethod(handler, method, mapping); this.updateConsumesCondition(mapping, method); }
4. org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register
public void register(T mapping, Object handler, Method method) { if (KotlinDetector.isKotlinType(method.getDeclaringClass())) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length > 0 && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) { throw new IllegalStateException("Unsupported suspending handler method detected: " + method); } } this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method); this.validateMethodMapping(handlerMethod, mapping); this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = this.getDirectUrls(mapping); Iterator var6 = directUrls.iterator(); while(var6.hasNext()) { String url = (String)var6.next(); this.urlLookup.add(url, mapping); } String name = null; if (AbstractHandlerMethodMapping.this.getNamingStrategy() != null) { name = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping); this.addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
这里维护到相关的map中。加到了mappingLookup<RequestMappingInfo, HandlerMethod> 和 urlLookup
补充: HandlerMethod 是一个重要的对象,用于后期调用方法的时候拿到该对象获取相关bean以及方法、参数信息进行调用
org.springframework.web.method.HandlerMethod 源码如下:
package org.springframework.web.method; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.StringJoiner; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ResponseStatus; /** * Encapsulates information about a handler method consisting of a * {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}. * Provides convenient access to method parameters, the method return value, * method annotations, etc. * * <p>The class may be created with a bean instance or with a bean name * (e.g. lazy-init bean, prototype bean). Use {@link #createWithResolvedBean()} * to obtain a {@code HandlerMethod} instance with a bean instance resolved * through the associated {@link BeanFactory}. * * @author Arjen Poutsma * @author Rossen Stoyanchev * @author Juergen Hoeller * @author Sam Brannen * @since 3.1 */ public class HandlerMethod { /** Logger that is available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); private final Object bean; @Nullable private final BeanFactory beanFactory; private final Class<?> beanType; private final Method method; private final Method bridgedMethod; private final MethodParameter[] parameters; @Nullable private HttpStatus responseStatus; @Nullable private String responseStatusReason; @Nullable private HandlerMethod resolvedFromHandlerMethod; @Nullable private volatile List<Annotation[][]> interfaceParameterAnnotations; private final String description; /** * Create an instance from a bean instance and a method. */ public HandlerMethod(Object bean, Method method) { Assert.notNull(bean, "Bean is required"); Assert.notNull(method, "Method is required"); this.bean = bean; this.beanFactory = null; this.beanType = ClassUtils.getUserClass(bean); this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); evaluateResponseStatus(); this.description = initDescription(this.beanType, this.method); } /** * Create an instance from a bean instance, method name, and parameter types. * @throws NoSuchMethodException when the method cannot be found */ public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException { Assert.notNull(bean, "Bean is required"); Assert.notNull(methodName, "Method name is required"); this.bean = bean; this.beanFactory = null; this.beanType = ClassUtils.getUserClass(bean); this.method = bean.getClass().getMethod(methodName, parameterTypes); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method); this.parameters = initMethodParameters(); evaluateResponseStatus(); this.description = initDescription(this.beanType, this.method); } /** * Create an instance from a bean name, a method, and a {@code BeanFactory}. * The method {@link #createWithResolvedBean()} may be used later to * re-create the {@code HandlerMethod} with an initialized bean. */ public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) { Assert.hasText(beanName, "Bean name is required"); Assert.notNull(beanFactory, "BeanFactory is required"); Assert.notNull(method, "Method is required"); this.bean = beanName; this.beanFactory = beanFactory; Class<?> beanType = beanFactory.getType(beanName); if (beanType == null) { throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'"); } this.beanType = ClassUtils.getUserClass(beanType); this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); evaluateResponseStatus(); this.description = initDescription(this.beanType, this.method); } /** * Copy constructor for use in subclasses. */ protected HandlerMethod(HandlerMethod handlerMethod) { Assert.notNull(handlerMethod, "HandlerMethod is required"); this.bean = handlerMethod.bean; this.beanFactory = handlerMethod.beanFactory; this.beanType = handlerMethod.beanType; this.method = handlerMethod.method; this.bridgedMethod = handlerMethod.bridgedMethod; this.parameters = handlerMethod.parameters; this.responseStatus = handlerMethod.responseStatus; this.responseStatusReason = handlerMethod.responseStatusReason; this.description = handlerMethod.description; this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod; } /** * Re-create HandlerMethod with the resolved handler. */ private HandlerMethod(HandlerMethod handlerMethod, Object handler) { Assert.notNull(handlerMethod, "HandlerMethod is required"); Assert.notNull(handler, "Handler object is required"); this.bean = handler; this.beanFactory = handlerMethod.beanFactory; this.beanType = handlerMethod.beanType; this.method = handlerMethod.method; this.bridgedMethod = handlerMethod.bridgedMethod; this.parameters = handlerMethod.parameters; this.responseStatus = handlerMethod.responseStatus; this.responseStatusReason = handlerMethod.responseStatusReason; this.resolvedFromHandlerMethod = handlerMethod; this.description = handlerMethod.description; } private MethodParameter[] initMethodParameters() { int count = this.bridgedMethod.getParameterCount(); MethodParameter[] result = new MethodParameter[count]; for (int i = 0; i < count; i++) { result[i] = new HandlerMethodParameter(i); } return result; } private void evaluateResponseStatus() { ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class); if (annotation == null) { annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class); } if (annotation != null) { this.responseStatus = annotation.code(); this.responseStatusReason = annotation.reason(); } } private static String initDescription(Class<?> beanType, Method method) { StringJoiner joiner = new StringJoiner(", ", "(", ")"); for (Class<?> paramType : method.getParameterTypes()) { joiner.add(paramType.getSimpleName()); } return beanType.getName() + "#" + method.getName() + joiner.toString(); } /** * Return the bean for this handler method. */ public Object getBean() { return this.bean; } /** * Return the method for this handler method. */ public Method getMethod() { return this.method; } /** * This method returns the type of the handler for this handler method. * <p>Note that if the bean type is a CGLIB-generated class, the original * user-defined class is returned. */ public Class<?> getBeanType() { return this.beanType; } /** * If the bean method is a bridge method, this method returns the bridged * (user-defined) method. Otherwise it returns the same method as {@link #getMethod()}. */ protected Method getBridgedMethod() { return this.bridgedMethod; } /** * Return the method parameters for this handler method. */ public MethodParameter[] getMethodParameters() { return this.parameters; } /** * Return the specified response status, if any. * @since 4.3.8 * @see ResponseStatus#code() */ @Nullable protected HttpStatus getResponseStatus() { return this.responseStatus; } /** * Return the associated response status reason, if any. * @since 4.3.8 * @see ResponseStatus#reason() */ @Nullable protected String getResponseStatusReason() { return this.responseStatusReason; } /** * Return the HandlerMethod return type. */ public MethodParameter getReturnType() { return new HandlerMethodParameter(-1); } /** * Return the actual return value type. */ public MethodParameter getReturnValueType(@Nullable Object returnValue) { return new ReturnValueMethodParameter(returnValue); } /** * Return {@code true} if the method return type is void, {@code false} otherwise. */ public boolean isVoid() { return Void.TYPE.equals(getReturnType().getParameterType()); } /** * Return a single annotation on the underlying method traversing its super methods * if no annotation can be found on the given method itself. * <p>Also supports <em>merged</em> composed annotations with attribute * overrides as of Spring Framework 4.2.2. * @param annotationType the type of annotation to introspect the method for * @return the annotation, or {@code null} if none found * @see AnnotatedElementUtils#findMergedAnnotation */ @Nullable public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) { return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType); } /** * Return whether the parameter is declared with the given annotation type. * @param annotationType the annotation type to look for * @since 4.3 * @see AnnotatedElementUtils#hasAnnotation */ public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) { return AnnotatedElementUtils.hasAnnotation(this.method, annotationType); } /** * Return the HandlerMethod from which this HandlerMethod instance was * resolved via {@link #createWithResolvedBean()}. */ @Nullable public HandlerMethod getResolvedFromHandlerMethod() { return this.resolvedFromHandlerMethod; } /** * If the provided instance contains a bean name rather than an object instance, * the bean name is resolved before a {@link HandlerMethod} is created and returned. */ public HandlerMethod createWithResolvedBean() { Object handler = this.bean; if (this.bean instanceof String) { Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory"); String beanName = (String) this.bean; handler = this.beanFactory.getBean(beanName); } return new HandlerMethod(this, handler); } /** * Return a short representation of this handler method for log message purposes. * @since 4.3 */ public String getShortLogMessage() { return getBeanType().getName() + "#" + this.method.getName() + "[" + this.method.getParameterCount() + " args]"; } private List<Annotation[][]> getInterfaceParameterAnnotations() { List<Annotation[][]> parameterAnnotations = this.interfaceParameterAnnotations; if (parameterAnnotations == null) { parameterAnnotations = new ArrayList<>(); for (Class<?> ifc : ClassUtils.getAllInterfacesForClassAsSet(this.method.getDeclaringClass())) { for (Method candidate : ifc.getMethods()) { if (isOverrideFor(candidate)) { parameterAnnotations.add(candidate.getParameterAnnotations()); } } } this.interfaceParameterAnnotations = parameterAnnotations; } return parameterAnnotations; } private boolean isOverrideFor(Method candidate) { if (!candidate.getName().equals(this.method.getName()) || candidate.getParameterCount() != this.method.getParameterCount()) { return false; } Class<?>[] paramTypes = this.method.getParameterTypes(); if (Arrays.equals(candidate.getParameterTypes(), paramTypes)) { return true; } for (int i = 0; i < paramTypes.length; i++) { if (paramTypes[i] != ResolvableType.forMethodParameter(candidate, i, this.method.getDeclaringClass()).resolve()) { return false; } } return true; } @Override public boolean equals(@Nullable Object other) { if (this == other) { return true; } if (!(other instanceof HandlerMethod)) { return false; } HandlerMethod otherMethod = (HandlerMethod) other; return (this.bean.equals(otherMethod.bean) && this.method.equals(otherMethod.method)); } @Override public int hashCode() { return (this.bean.hashCode() * 31 + this.method.hashCode()); } @Override public String toString() { return this.description; } // Support methods for use in "InvocableHandlerMethod" sub-class variants.. @Nullable protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) { if (!ObjectUtils.isEmpty(providedArgs)) { for (Object providedArg : providedArgs) { if (parameter.getParameterType().isInstance(providedArg)) { return providedArg; } } } return null; } protected static String formatArgumentError(MethodParameter param, String message) { return "Could not resolve parameter [" + param.getParameterIndex() + "] in " + param.getExecutable().toGenericString() + (StringUtils.hasText(message) ? ": " + message : ""); } /** * Assert that the target bean class is an instance of the class where the given * method is declared. In some cases the actual controller instance at request- * processing time may be a JDK dynamic proxy (lazy initialization, prototype * beans, and others). {@code @Controller}'s that require proxying should prefer * class-based proxy mechanisms. */ protected void assertTargetBean(Method method, Object targetBean, Object[] args) { Class<?> methodDeclaringClass = method.getDeclaringClass(); Class<?> targetBeanClass = targetBean.getClass(); if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { String text = "The mapped handler method class '" + methodDeclaringClass.getName() + "' is not an instance of the actual controller bean class '" + targetBeanClass.getName() + "'. If the controller requires proxying " + "(e.g. due to @Transactional), please use class-based proxying."; throw new IllegalStateException(formatInvokeError(text, args)); } } protected String formatInvokeError(String text, Object[] args) { String formattedArgs = IntStream.range(0, args.length) .mapToObj(i -> (args[i] != null ? "[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" : "[" + i + "] [null]")) .collect(Collectors.joining(",\n", " ", " ")); return text + "\n" + "Controller [" + getBeanType().getName() + "]\n" + "Method [" + getBridgedMethod().toGenericString() + "] " + "with argument values:\n" + formattedArgs; } /** * A MethodParameter with HandlerMethod-specific behavior. */ protected class HandlerMethodParameter extends SynthesizingMethodParameter { @Nullable private volatile Annotation[] combinedAnnotations; public HandlerMethodParameter(int index) { super(HandlerMethod.this.bridgedMethod, index); } protected HandlerMethodParameter(HandlerMethodParameter original) { super(original); } @Override public Class<?> getContainingClass() { return HandlerMethod.this.getBeanType(); } @Override public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) { return HandlerMethod.this.getMethodAnnotation(annotationType); } @Override public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) { return HandlerMethod.this.hasMethodAnnotation(annotationType); } @Override public Annotation[] getParameterAnnotations() { Annotation[] anns = this.combinedAnnotations; if (anns == null) { anns = super.getParameterAnnotations(); int index = getParameterIndex(); if (index >= 0) { for (Annotation[][] ifcAnns : getInterfaceParameterAnnotations()) { if (index < ifcAnns.length) { Annotation[] paramAnns = ifcAnns[index]; if (paramAnns.length > 0) { List<Annotation> merged = new ArrayList<>(anns.length + paramAnns.length); merged.addAll(Arrays.asList(anns)); for (Annotation paramAnn : paramAnns) { boolean existingType = false; for (Annotation ann : anns) { if (ann.annotationType() == paramAnn.annotationType()) { existingType = true; break; } } if (!existingType) { merged.add(adaptAnnotation(paramAnn)); } } anns = merged.toArray(new Annotation[0]); } } } } this.combinedAnnotations = anns; } return anns; } @Override public HandlerMethodParameter clone() { return new HandlerMethodParameter(this); } } /** * A MethodParameter for a HandlerMethod return type based on an actual return value. */ private class ReturnValueMethodParameter extends HandlerMethodParameter { @Nullable private final Object returnValue; public ReturnValueMethodParameter(@Nullable Object returnValue) { super(-1); this.returnValue = returnValue; } protected ReturnValueMethodParameter(ReturnValueMethodParameter original) { super(original); this.returnValue = original.returnValue; } @Override public Class<?> getParameterType() { return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType()); } @Override public ReturnValueMethodParameter clone() { return new ReturnValueMethodParameter(this); } } }
比如调用过程中从RequestMappingHandlerMapping 获取到的一个HandlerMethod 如下:
补充: 如果一个Servlet的拦截路径为/,是会作为Tomcat的默认的Servlet最后进行匹配
1. 解析默认Servlet如下: 也可以看到Tomcat 启动过程中注册Servlet以及其他相关组件的调用链
org.apache.catalina.mapper.Mapper#addWrapper(org.apache.catalina.mapper.Mapper.ContextVersion, java.lang.String, org.apache.catalina.Wrapper, boolean, boolean)
protected void addWrapper(ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) { synchronized (context) { if (path.endsWith("/*")) { // Wildcard wrapper String name = path.substring(0, path.length() - 2); MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.wildcardWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.wildcardWrappers = newWrappers; int slashCount = slashCount(newWrapper.name); if (slashCount > context.nesting) { context.nesting = slashCount; } } } else if (path.startsWith("*.")) { // Extension wrapper String name = path.substring(2); MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.extensionWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.extensionWrappers = newWrappers; } } else if (path.equals("/")) { // Default wrapper MappedWrapper newWrapper = new MappedWrapper("", wrapper, jspWildCard, resourceOnly); context.defaultWrapper = newWrapper; } else { // Exact wrapper final String name; if (path.length() == 0) { // Special case for the Context Root mapping which is // treated as an exact match name = "/"; } else { name = path; } MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.exactWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.exactWrappers = newWrappers; } } } }
SoringMVC的url为/, 所以其是默认的defaultWrapper。调用链条如下:
2. 调用过程中匹配Servlet 方法如下:(进入SpringMVC的DispatcherServlet原理)
org.apache.catalina.mapper.Mapper#internalMapWrapper 寻找org.apache.catalina.Wrapper对象,找到之后记录到mappingData.wrapper 供后面业务使用
private final void internalMapWrapper(ContextVersion contextVersion, CharChunk path, MappingData mappingData) throws IOException { int pathOffset = path.getOffset(); int pathEnd = path.getEnd(); boolean noServletPath = false; int length = contextVersion.path.length(); if (length == (pathEnd - pathOffset)) { noServletPath = true; } int servletPath = pathOffset + length; path.setOffset(servletPath); // Rule 1 -- Exact Match MappedWrapper[] exactWrappers = contextVersion.exactWrappers; internalMapExactWrapper(exactWrappers, path, mappingData); // Rule 2 -- Prefix Match boolean checkJspWelcomeFiles = false; MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers; if (mappingData.wrapper == null) { internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting, path, mappingData); if (mappingData.wrapper != null && mappingData.jspWildCard) { char[] buf = path.getBuffer(); if (buf[pathEnd - 1] == '/') { /* * Path ending in '/' was mapped to JSP servlet based on * wildcard match (e.g., as specified in url-pattern of a * jsp-property-group. * Force the context's welcome files, which are interpreted * as JSP files (since they match the url-pattern), to be * considered. See Bugzilla 27664. */ mappingData.wrapper = null; checkJspWelcomeFiles = true; } else { // See Bugzilla 27704 mappingData.wrapperPath.setChars(buf, path.getStart(), path.getLength()); mappingData.pathInfo.recycle(); } } } if(mappingData.wrapper == null && noServletPath && contextVersion.object.getMapperContextRootRedirectEnabled()) { // The path is empty, redirect to "/" path.append('/'); pathEnd = path.getEnd(); mappingData.redirectPath.setChars (path.getBuffer(), pathOffset, pathEnd - pathOffset); path.setEnd(pathEnd - 1); return; } // Rule 3 -- Extension Match MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers; if (mappingData.wrapper == null && !checkJspWelcomeFiles) { internalMapExtensionWrapper(extensionWrappers, path, mappingData, true); } // Rule 4 -- Welcome resources processing for servlets if (mappingData.wrapper == null) { boolean checkWelcomeFiles = checkJspWelcomeFiles; if (!checkWelcomeFiles) { char[] buf = path.getBuffer(); checkWelcomeFiles = (buf[pathEnd - 1] == '/'); } if (checkWelcomeFiles) { for (int i = 0; (i < contextVersion.welcomeResources.length) && (mappingData.wrapper == null); i++) { path.setOffset(pathOffset); path.setEnd(pathEnd); path.append(contextVersion.welcomeResources[i], 0, contextVersion.welcomeResources[i].length()); path.setOffset(servletPath); // Rule 4a -- Welcome resources processing for exact macth internalMapExactWrapper(exactWrappers, path, mappingData); // Rule 4b -- Welcome resources processing for prefix match if (mappingData.wrapper == null) { internalMapWildcardWrapper (wildcardWrappers, contextVersion.nesting, path, mappingData); } // Rule 4c -- Welcome resources processing // for physical folder if (mappingData.wrapper == null && contextVersion.resources != null) { String pathStr = path.toString(); WebResource file = contextVersion.resources.getResource(pathStr); if (file != null && file.isFile()) { internalMapExtensionWrapper(extensionWrappers, path, mappingData, true); if (mappingData.wrapper == null && contextVersion.defaultWrapper != null) { mappingData.wrapper = contextVersion.defaultWrapper.object; mappingData.requestPath.setChars (path.getBuffer(), path.getStart(), path.getLength()); mappingData.wrapperPath.setChars (path.getBuffer(), path.getStart(), path.getLength()); mappingData.requestPath.setString(pathStr); mappingData.wrapperPath.setString(pathStr); } } } } path.setOffset(servletPath); path.setEnd(pathEnd); } } /* welcome file processing - take 2 * Now that we have looked for welcome files with a physical * backing, now look for an extension mapping listed * but may not have a physical backing to it. This is for * the case of index.jsf, index.do, etc. * A watered down version of rule 4 */ if (mappingData.wrapper == null) { boolean checkWelcomeFiles = checkJspWelcomeFiles; if (!checkWelcomeFiles) { char[] buf = path.getBuffer(); checkWelcomeFiles = (buf[pathEnd - 1] == '/'); } if (checkWelcomeFiles) { for (int i = 0; (i < contextVersion.welcomeResources.length) && (mappingData.wrapper == null); i++) { path.setOffset(pathOffset); path.setEnd(pathEnd); path.append(contextVersion.welcomeResources[i], 0, contextVersion.welcomeResources[i].length()); path.setOffset(servletPath); internalMapExtensionWrapper(extensionWrappers, path, mappingData, false); } path.setOffset(servletPath); path.setEnd(pathEnd); } } // Rule 7 -- Default servlet if (mappingData.wrapper == null && !checkJspWelcomeFiles) { if (contextVersion.defaultWrapper != null) { mappingData.wrapper = contextVersion.defaultWrapper.object; mappingData.requestPath.setChars (path.getBuffer(), path.getStart(), path.getLength()); mappingData.wrapperPath.setChars (path.getBuffer(), path.getStart(), path.getLength()); mappingData.matchType = MappingMatch.DEFAULT; } // Redirection to a folder char[] buf = path.getBuffer(); if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') { String pathStr = path.toString(); WebResource file; // Handle context root if (pathStr.length() == 0) { file = contextVersion.resources.getResource("/"); } else { file = contextVersion.resources.getResource(pathStr); } if (file != null && file.isDirectory() && contextVersion.object.getMapperDirectoryRedirectEnabled()) { // Note: this mutates the path: do not do any processing // after this (since we set the redirectPath, there // shouldn't be any) path.setOffset(pathOffset); path.append('/'); mappingData.redirectPath.setChars (path.getBuffer(), path.getStart(), path.getLength()); } else { mappingData.requestPath.setString(pathStr); mappingData.wrapperPath.setString(pathStr); } } } path.setOffset(pathOffset); path.setEnd(pathEnd); }
可以看到匹配规则,先全匹配,匹配不到之后用默认的Servlet(拦截路径为/的进行处理)。
补充: 关于DispatcherServlet 内部初始化
SpringMVC的DispatcherServlet 在第一次请求的时候进行初始化内部相关的配置类信息,org.springframework.web.servlet.DispatcherServlet#initStrategies。 比如初始化文件解析器 multipartResolver方法如下: org.springframework.web.servlet.DispatcherServlet#initMultipartResolver
/** * Initialize the MultipartResolver used by this class. * <p>If no bean is defined with the given name in the BeanFactory for this namespace, * no multipart handling is provided. */ private void initMultipartResolver(ApplicationContext context) { try { this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isTraceEnabled()) { logger.trace("Detected " + this.multipartResolver); } else if (logger.isDebugEnabled()) { logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName()); } } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isTraceEnabled()) { logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared"); } } }
其他获取方式也大体类似。都是从容器中获取相关的对象,如果获取不到就设为null,在后面请求分发doDispatch 的过程中用对应的对象进行操作。