在Spring MVC 3.1 之前的版本中,Spring默认使用 DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter来处理 @RequestMapping注解和请求方法调用,而从3.1开始提供了一组新的API,RequestMappingHandlerMapping和RequestMappingHandlerAdapter完成这些工作。
HandlerMapping 叫做处理器映射器,它的作用就是根据当前 request 找到对应的 Handler 和 Interceptor,然后封装成一个 HandlerExecutionChain 对象返回。
其提供了一个方法HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
供子类实现。 我们这里先看下关于请求映射处理的家族树图示:
从上图可以看到,请求映射的家族从该类主要分为三支:根据方法进行匹配的AbstractHandlerMethodMapping、RouterFunctionMapping以及根据请求URL进行批的AbstractUrlHandlerMapping。其中RouterFunctionMapping是5.2版本后引入的,这里暂不分析。
【1】 AbstractHandlerMapping
AbstractHandlerMapping如下所示,其提供了系列模板方法以及抽象方法。
// 返回HandlerInterceptor数组 protected final HandlerInterceptor[] getAdaptedInterceptors() {} // 返回 HandlerExecutionChain 其包括handler HandlerInterceptor public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {} // 返回所有配置的MappedInterceptor protected final MappedInterceptor[] getMappedInterceptors() {} // 抽象方法供子类实现 ,返回一个handler protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
AbstractHandlerMapping 间接继承自 ApplicationObjectSupport,并重写了 initApplicationContext 方法(其实该方法也是一个模版方法),这也是 AbstractHandlerMapping 的初始化入口方法,该方法主要就是扫描容器中的拦截器然后放到全局的private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();变量中。
因为ApplicationObjectSupport实现了ApplicationContextAware接口,故而在bean的实例化过程中,凡是实现了ApplicationContextAware接口的类都会被调用其setApplicationContext方法。ApplicationObjectSupport的setApplicationContext又提供了initApplicationContext方法供子类实现。也就是说ApplicationObjectSupport的子类实例化时会被调用initApplicationContext方法。
如下方法会在AbstractHandlerMapping的子类如SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping、RequestMappingHandlerMapping等实例化时被调用。
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }
① 核心方法getHandler
再看下核心方法public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {}
@Override @Nullable 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 = obtainApplicationContext().getBean(handlerName); } // 根据handler request获取HandlerExecutionChain 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 (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = getCorsConfiguration(handler, request); if (getCorsConfigurationSource() != null) { CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request); config = (globalConfig != null ? globalConfig.combine(config) : config); } if (config != null) { config.validateAllowCredentials(); } executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
方法解释如下:
① 获取handler ;
② 如果① 获取的handler为null,则获取一个默认的handler也就是defaultHandler;
③ 如果②仍旧没有获取到handler,则返回null;
④ 根据handler获取HandlerExecutionChain,其包括了handler和拦截器;
⑤ 如果进行了跨域配置,则获取CorsHandlerExecutionChain
② 核心方法getHandlerExecutionChain
我们看内部的核心方法getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(request)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
方法解释如下:
① 处理获取一个HandlerExecutionChain ;
② 遍历adaptedInterceptors
③ 如果interceptor 是 MappedInterceptor则判断是否匹配当前请求,匹配则添加到当前处理器链中
④ 如果interceptor 是普通拦截器(没有指定映射URL则默认拦截所有),则直接添加到当前处理器链中
【2】 AbstractUrlHandlerMapping
该分支下是根据URL进行处理器映射匹配查找。常见实现子类有SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping。其覆盖了父类AbstractHandlerMapping的getHandlerInternal方法。
如下所示,其内部维护了两个final map:handlerMap 维护了一个URL与handler的映射关系;pathPatternHandlerMap 维护了一个PathPattern与handler的映射关系。当获取handler时就从这两个map中进行遍历查找。
// URL模式--handler private final Map<String, Object> handlerMap = new LinkedHashMap<>(); // PathPattern模式--handler private final Map<PathPattern, Object> pathPatternHandlerMap = new LinkedHashMap<>();
① 核心方法getHandlerInternal
getHandlerInternal
方法会根据request获取一个handler。
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = initLookupPath(request); Object handler; if (usesPathPatterns()) { RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request); handler = lookupHandler(path, lookupPath, request); } else { handler = lookupHandler(lookupPath, request); } if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if (StringUtils.matchesCharacter(lookupPath, '/')) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = obtainApplicationContext().getBean(handlerName); } // 这里默认是空方法 validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; }
方法解释如下:
① 根据request获取lookupPath;
② 如果patternParser不为null,调用lookupHandler(path, lookupPath, request)也就是从pathPatternHandlerMap寻找handler;
③ 如果patternParser为null,则调用lookupHandler(lookupPath, request)也就是从handlerMap进行查找;
④ 如果②③之后handler还为null,则尝试获取RootHandler、DefaultHandler。
⑤ 如果找到的 Handler 是一个 String,则去 Spring 容器中查找该 String 对应的 Bean;
⑥ 调用 validateHandler 方法来校验找到的 handler 和 request 是否匹配,这是一个空方法,可以忽略。
⑦ 通过 buildPathExposingHandler 方法将找到的handler包装为HandlerExecutionChain然后添加PathExposingHandlerInterceptor拦截器,并根据uriTemplateVariables是否为空添加UriTemplateVariablesHandlerInterceptor拦截器。
② 核心方法lookupHandler(String lookupPath, HttpServletRequest request)
也就是根据URL获取一个handler。
@Nullable protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception { Object handler = getDirectMatch(lookupPath, request); if (handler != null) { return handler; } // Pattern match? 也就是通配符匹配 List<String> matchingPatterns = new ArrayList<>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, lookupPath)) { matchingPatterns.add(registeredPattern); } else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) { matchingPatterns.add(registeredPattern + "/"); } } } String bestMatch = null; // AntPathMatcher.AntPatternComparator Comparator<String> patternComparator = getPathMatcher().getPatternComparator(lookupPath); if (!matchingPatterns.isEmpty()) { matchingPatterns.sort(patternComparator); if (logger.isTraceEnabled() && matchingPatterns.size() > 1) { logger.trace("Matching patterns " + matchingPatterns); } bestMatch = matchingPatterns.get(0); } if (bestMatch != null) { handler = this.handlerMap.get(bestMatch); if (handler == null) { if (bestMatch.endsWith("/")) { handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1)); } if (handler == null) { throw new IllegalStateException( "Could not find handler for best pattern match [" + bestMatch + "]"); } } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, lookupPath); // There might be multiple 'best patterns', let's make sure we have the correct URI template variables // for all of them Map<String, String> uriTemplateVariables = new LinkedHashMap<>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, lookupPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) { logger.trace("URI variables " + uriTemplateVariables); } return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }
方法解释如下:
① 根据lookupPath从handlerMap中获取对应的handler,如果存在则直接返回。注意,这里是直接匹配,也就是没有通配符。
② 遍历handlerMap:
③ 如果当前key与lookupPath匹配(由AntPathMatcher决定),则添加当前key到List<String> matchingPatterns中;
④ 如果当前key+"/" 与lookupPath匹配(由AntPathMatcher决定),则添加当前key+"/"到List<String> matchingPatterns中;
⑤ 根据PathMatcher获取PatternComparator,默认是AntPatternComparator;
⑥ 如果matchingPatterns不为空,则根据PatternComparator进行排序然后获取第一个元素赋予bestMatch;
⑦ 如果获取的bestMatch不为空则从handlerMap中获取对应的handler;
⑧ 如果⑦获取的handler为空且bestMatch.endsWith("/"),则bestMatch去掉"/"再次从handlerMap获取handler;
⑨ 如果⑧获取的handler仍旧为null,则抛出异常。
(10) 如果找到的 handler 是 String 类型的,则再去 Spring 容器中查找对应的 Bean,接下来再调用 validateHandler 方法进行验证;
(11) extractPathWithinPattern 方法提取出匹配部分pattern-matched路径。例如定义的接口规则是 myroot/*.html,请求路径是 myroot/myfile.html,那么最终获取到的就是 myfile.html。
(12) 遍历matchingPatterns,然后patternComparator.compare(bestMatch, matchingPattern) == 0 也就是判断当前matchingPattern是否与bestMatch等价。然后根据每一个matchingPattern与lookupPath尝试解析path里面变量放入Map<String, String> uriTemplateVariables;
(13) 调用buildPathExposingHandler,这里不再赘述。
(14) 如果获取的bestMatch为空,则直接返回null。
由于一个请求 可能会和定义的多个接口匹配上(接口定义的URL带有通配符),所以 matchingPatterns 变量是一个数组。接下来就要对 matchingPatterns 进行排序,排序完成后,选择排序后的第一项作为最佳选项赋值给 bestMatch 变量。默认的排序规则是 AntPatternComparator,当然开发者也可以自定义。AntPatternComparator 中定义的优先级如下:
URL | 优先级 |
不含任何特殊符号的路径,如:/a/b/c |
第一优先级 |
带有{} 的路径,如:/a/{b}/c |
第二优先级 |
带有正则的路径,如:/a/{regex:\d{3}}/c |
第三优先级 |
带有* 的路径,如:/a/b/* |
第四优先级 |
带有** 的路径,如:/a/b/** |
第五优先级 |
最模糊的匹配:/** |
最低优先级 |
③ 核心方法registerHandler
该方法就是扫描并注册handler,在bean实例化时被调用,如下所示。其中detectHandlers会调用registerHandler(urls, beanName)
进行遍历循环注册。
@Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext(); detectHandlers(); }
这里我们直接看registerHandler(urlPath, beanName)
方法,其尝试将urlPath = resolvedHandler
放入handlerMap中。
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isTraceEnabled()) { logger.trace("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isTraceEnabled()) { logger.trace("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); if (getPatternParser() != null) { this.pathPatternHandlerMap.put(getPatternParser().parse(urlPath), resolvedHandler); } if (logger.isTraceEnabled()) { logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
方法源码解释如下:
① Object resolvedHandler = handler,如果!this.lazyInitHandlers && handler instanceof String,则尝试从Spring容器中获取resolvedHandler;
② 根据urlPath尝试从handlerMap获取handler,如果handler不为空且不等于入参中的handler则抛出异常。如果不为空且等于入参中的handler,则表示已经存在不做处理。
③ 如果从handlerMap获取不到handler:
④ 如果urlPath.equals("/"),则将resolvedHandler设置为RootHandler;
⑤ urlPath.equals("/*"),则将resolvedHandler设置为DefaultHandler;
⑥ this.handlerMap.put(urlPath, resolvedHandler);将其放入handlerMap。
【3】SimpleUrlHandlerMapping
顾名思义,就是在URL与实例bean(或bean 名称)维护映射关系。其内部维护了一个final mapprivate final Map<String, Object> urlMap = new LinkedHashMap<>();。
其构造方法如下所示:
private final Map<String, Object> urlMap = new LinkedHashMap<>(); // 空的构造函数 public SimpleUrlHandlerMapping() { } //根据urlMap 初始化 final Map<String, Object> urlMap public SimpleUrlHandlerMapping(Map<String, ?> urlMap) { setUrlMap(urlMap); } // 在前者的基础上并设置当前handleMapping的order public SimpleUrlHandlerMapping(Map<String, ?> urlMap, int order) { setUrlMap(urlMap); setOrder(order); }
其重写了父类的initApplicationContext
方法,在该方法中调用了父类initApplicationContext
方法,并调用了registerHandlers(this.urlMap);
。
@Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); }
① 核心方法registerHandlers
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.trace("No patterns in " + formatMappingName()); } else { urlMap.forEach((url, handler) -> { // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); }); if (logger.isDebugEnabled()) { List<String> patterns = new ArrayList<>(); if (getRootHandler() != null) { patterns.add("/"); } if (getDefaultHandler() != null) { patterns.add("/**"); } patterns.addAll(getHandlerMap().keySet()); logger.debug("Patterns " + patterns + " in " + formatMappingName()); } } }
方法解释如下:
① 如果urlMap为空,则直接返回;
② 遍历循环urlMap,如果当前URL不是以"/"开头,则拼接前缀"/";如果handler是string,则进行trim;然后调用父类AbstractUrlHandlerMapping的registerHandler方法。
② SimpleUrlHandlerMapping的使用
一个应用实例是如下配置,则这里urlMap就维护了映射关系。
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
也就是说,如果你想使用SimpleUrlHandlerMapping,那么请自行配置映射关系。如下xml配置:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlMap"> <map> <entry key="/hello" value="helloController"/> </map> </property> <!--或者如下配置--> <property name="mappings"> <props> <prop key="/hello">helloController</prop> </props> </property> <!--或者如下配置--> <property name="mappings"> <value> /hello=helloController </value> </property> </bean>
helloController是controller的名字,需要实现Controller接口,如下所示:
@org.springframework.stereotype.Controller public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println(request.getRequestURI()); System.out.println(this.getClass().getName()); ModelAndView modelAndView=new ModelAndView(); modelAndView.setViewName("success"); return modelAndView; } }
如果启用了defaultServletHandler
那么SimpleUrlHandlerMapping
的urlMap
中还有如下配置:
/**=org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0
【4】AbstractDetectingUrlHandlerMapping
这个简单来讲,就是扫描spring容器中注册的bean。如果bean name或bean的别名是以"/"开头,那么就将bean name或bean的别名作为URL,调用父类AbstractUrlHandlerMapping的registerHandler(String[] urlPaths, String beanName)方法最后注册到handlerMap中。
我们同样从initApplicationContext方法看起,其调用了父类的AbstractHandlerMapping的initApplicationContext方法并调用detectHandlers来检测handler。
@Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext(); detectHandlers(); }
① 核心方法detectHandlers
protected void detectHandlers() throws BeansException { ApplicationContext applicationContext = obtainApplicationContext(); String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class)); // Take any bean name that we can determine URLs for. for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { // URL paths found: Let's consider it a handler. registerHandler(urls, beanName); } } // ... 这里是无需关注的日志打印 } prote
方法解释如下:
- ① 获取ApplicationContext;
- ② 获取容器中的beanNames ,这里得到一个数组;
- ③ 遍历beanNames ,检测其URL。如果URL不为空则注册到handlerMap。
这里determineUrlsForHandler
是个抽象方法,供子类实现。其只有一个子类BeanNameUrlHandlerMapping
,我们看下其实现determineUrlsForHandler方法。
@Override protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<>(); if (beanName.startsWith("/")) { urls.add(beanName); } String[] aliases = obtainApplicationContext().getAliases(beanName); for (String alias : aliases) { if (alias.startsWith("/")) { urls.add(alias); } } return StringUtils.toStringArray(urls); } @
方法很简单,判断beanName是否以"/"
开头,是的话放入urls数组中。然后获取beanName
的别名,对别名数组进行遍历判断是否以否以"/"开头,是的话放入urls数组中。
也就是说,上面我们的HelloController可以指定名字如下所示:
@org.springframework.stereotype.Controller("/helloController") public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println(request.getRequestURI()); System.out.println(this.getClass().getName()); ModelAndView modelAndView=new ModelAndView(); modelAndView.setViewName("success"); return modelAndView; } }
【5】AbstractHandlerMethodMapping
在前面 AbstractUrlHandlerMapping 体系下,一个 Handler 一般就是一个类(显示Controller如HelloController或者隐式controller如ParameterizableViewController)。但是在 AbstractHandlerMethodMapping 体系下,一个 Handler 通常指的是一个 Mehtod,是HandlerMethod类型。
AbstractHandlerMethodMapping主要维护了请求和handlerMethod之间的映射关系。其覆盖了父类AbstractHandlerMapping如下三个方法:
// 核心方法,获取内部handler。该体系下返回handlerMethod protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {} //如下两个方法和跨域配置有关 protected CorsConfiguration getCorsConfiguration(Object handler, HttpServletRequest request) {} protected boolean hasCorsConfigurationSource(Object handler) {}
其提供了如下抽象方法供子类实现
//RequestMappingInfoHandlerMapping实现 protected abstract T getMatchingMapping(T mapping, HttpServletRequest request); //RequestMappingInfoHandlerMapping实现 protected abstract Comparator<T> getMappingComparator(HttpServletRequest request); //RequestMappingHandlerMapping实现 protected abstract boolean isHandler(Class<?> beanType); //RequestMappingHandlerMapping实现 protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
核心变量private final MappingRegistry mappingRegistry = new MappingRegistry(); 为什么说是核心变量?我们可以看下mappingRegistry究竟是个什么?如下所示其registry 维护了所有的RequestMappingInfo实例与MappingRegistration映射关系。pathLookup 维护了directPath与List<RequestMappingInfo>实例关系。当处理请求获取handlerMethod时,qi
private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>(); private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
我们看一下该体系下的家族树图示
需要注意的是AbstractHandlerMethodMapping其实现了InitializingBean接口,重写了afterPropertiesSet方法。为什么这里特意提一下实现了InitializingBean接口,那是因为这个接口定义了其实现Bean在容器完成属性设置后可以执行自定义初始化操作,我们的AbstractHandlerMethodMapping便实现了这个接口,并且定义了一组自定义操作,就是用来检测处理我们的@RequestMapping注解。
① afterPropertiesSet
如下图所示,如何bean实现了InitializingBean
接口,那么在初始化过程中一定会调用其afterPropertiesSet
方法。
这里我们看一下RequestMappingHandlerMapping.afterPropertiesSet
方法。
@Override @SuppressWarnings("deprecation") public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setTrailingSlashMatch(useTrailingSlashMatch()); this.config.setContentNegotiationManager(getContentNegotiationManager()); if (getPatternParser() != null) { this.config.setPatternParser(getPatternParser()); Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch, "Suffix pattern matching not supported with PathPatternParser."); } else { this.config.setSuffixPatternMatch(useSuffixPatternMatch()); this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch()); this.config.setPathMatcher(getPathMatcher()); } super.afterPropertiesSet(); }
可以看到其主要做了一些配置如后缀模式匹配,然后调用父类AbstractHandlerMethodMapping
的afterPropertiesSet
方法。其源码如下所示-在初始化过程中检测处理器方法。
@Override public void afterPropertiesSet() { initHandlerMethods(); }
简单直接调用了AbstractHandlerMethodMapping#initHandlerMethods
方法。如下所示父类直接调用initHandlerMethods方法进行handlerMethod的初始化注册。
@Override public void afterPropertiesSet() { initHandlerMethods(); }
① initHandlerMethods
initHandlerMethods
方法源码如下:
① initHandlerMethods initHandlerMethods方法源码如下: // 在Ap
这里我们着重分析processCandidateBean(beanName);
和getHandlerMethods()
方法。
② processCandidateBean
processCandidateBean(beanName);
方法的作用是什么呢?判断其是否为一个handler
(也就是是否拥有@Controller
注解或@RequestMapping
注解)。如果是一个handler,则检测并注册其内部方法。
protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } } if (beanType != null && isHandler(beanType)) { // 注意,这里是核心 detectHandlerMethods(beanName); } }
isHandler(beanType)
是一个抽象方法,在子类RequestMappingHandlerMapping
中实现。
我们看下isHandler(beanType)
是如何判断当前bean是否为一个handler
的?
@Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
判断类上面的注解是否是(或拥有)@Controller或者@RequestMapping(或者复合注解如@RestController、@PostMapping)。OK,我们继续看detectHandlerMethods方法如何扫描并注册。
③ detectHandlerMethods
扫描handler内部的handler method然后注册到AbstractHandlerMethodMapping内部的常量private final MappingRegistry mappingRegistry = new MappingRegistry();中。
protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }
这里我们暂不展开分析,放在第三部分中展开。我们只需要知道在RequestMappingHandlerMapping 初始化过程中,该方法将会扫描容器中那些Controller及内部method。获取handler、method、mapping,也就是处理器、处理器方法与请求映射信息(包括@RequestMapping标注的那些参数)。
扫描并注册完之后,到了handlerMethodsInitialized(getHandlerMethods());。这里我们看下getHandlerMethods。
④ getHandlerMethods
AbstractHandlerMethodMapping#getHandlerMethods
方法如下:
// 返回只读的map mapping :HandlerMethod public Map<T, HandlerMethod> getHandlerMethods() { // private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); this.mappingRegistry.acquireReadLock(); try { return Collections.unmodifiableMap( this.mappingRegistry.getRegistrations().entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().handlerMethod))); } finally { this.mappingRegistry.releaseReadLock(); } }
方法源码解释如下:
获取mappingRegistry中的读锁然后加锁;
遍历MappingRegistry.registry,返回一个只读Map;
释放锁
handlerMethodsInitialized(getHandlerMethods());方法则更简单,只是简单打印告诉系统已经已经有多少个mappings被扫描注册。
【6】detectHandlerMethods详细分析
detectHandlerMethods方法是该组件的核心方法,将会扫描@RequestMapping注解的方法并创建RequestMappingInfo实例。然后将将RequestMappingInfo实例以及处理器方法注册到AbstractHandlerMethodMapping的内部类MappingRegistry中常量Mapprivate final Map<T, MappingRegistration<T>> registry = new HashMap<>();。
protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }
代码解释如下:
getMappingForMethod
方法创建RequestMappingInfo
实例;methods.forEach
方法循环遍历并注册handler
、invocableMethod
、mapping
到内存中。
这里我们可以先看下method、mapping的实例
如上图所示,method记录了方法详细信息诸如声明类、名字、返回结果类型、参数类型、异常类型、修饰符、注解、参数、是否覆盖等等。mapping则记录了@RequestMapping注解里面的信息如URL、请求方法、参数限制、header、consume、produce以及自定义限制条件等。
① getMappingForMethod
@Override @Nullable protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } String prefix = getPathPrefix(handlerType); if (prefix != null) { info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info); } } return info; }
代码解释如下:
① 根据method创建RequestMappingInfo 实例info ;
② 根据handlerType创建RequestMappingInfo 实例实例typeInfo ;
③ 如果②中创建的实例typeInfo 不为null,则info = typeInfo.combine(info)进行合并;
④ 根据handlerType获取类上面定义的路径前缀,如果前缀不为空则对info进行合并。
② 创建RequestMappingInfo实例
RequestMappingHandlerMapping#createRequestMappingInfo
源码如下所示:
@Nullable private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { // //获取方法method上的@RequestMapping实例。 RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); // 这里始终为null RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
然后我们继续看createRequestMappingInfo(requestMapping, condition)
方法。
protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { RequestMappingInfo.Builder builder = RequestMappingInfo .paths(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(); }
看着是不是会熟悉?其实就是获取@RequestMapping注解的url、params、headers、consumes、produces以及name最后进行创建RequestMappingInfo实例。
关于@RequestMapping注解的url、params、headers、consumes、produces以及name这些本文不做赘述,
@RequestMapping注解信息实例
@org.s@org.springframework.web.bind.annotation.RequestMapping(consumes=[], headers=[], method=[], name=, params=[], path=[/**/testInterceptor], produces=[], value=[/**/testInterceptor])
RequestMappingInfo实例
③ 类与方法上面的请求映射实例合并typeInfo.combine(info)
这里我们着重看一下根据类创建的RequestMappingInfo
实例与根据方法创建的RequestMappingInfo
实例合并过程。
@Override public RequestMappingInfo combine(RequestMappingInfo other) { String name = combineNames(other); PathPatternsRequestCondition pathPatterns = (this.pathPatternsCondition != null && other.pathPatternsCondition != null ? this.pathPatternsCondition.combine(other.pathPatternsCondition) : null); PatternsRequestCondition patterns = (this.patternsCondition != null && other.patternsCondition != null ? this.patternsCondition.combine(other.patternsCondition) : null); RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition); ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition); HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition); ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition); ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition); RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder); return new RequestMappingInfo( name, pathPatterns, patterns, methods, params, headers, consumes, produces, custom); }
这里涉及到了几个类,我们大致了解下含义:
PatternRequestCondition 它其实就是URL模式的封装,它包含了一个URL模式的Set集合。其实就是@RequestMapping注解中的value值得封装。
RequestMethodRequestCondition 它是@RequestMapping 注解中method属性的封装
ParamsRequestCondition 它是@RequestMapping注解中params属性的封装
等等,依次类推。因此RequestMappingInfo其实就是对@RquestMapping 的封装。这里关于method、params、heade等的合并我们暂时不理会(其实你对框架应用熟悉就应该能想到这些合并就是做条件与),着重看下patternsCondition的合并。
@Override public PatternsRequestCondition combine(PatternsRequestCondition other) { if (isEmptyPathMapping() && other.isEmptyPathMapping()) { return this; } else if (other.isEmptyPathMapping()) { return this; } else if (isEmptyPathMapping()) { return other; } Set<String> result = new LinkedHashSet<>(); if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) { for (String pattern1 : this.patterns) { for (String pattern2 : other.patterns) { result.add(this.pathMatcher.combine(pattern1, pattern2)); } } } return new PatternsRequestCondition(result, this); }
代码解释如下:
- 如果两个path都为空,则返回this;
- 如果其中一个为空,返回不为空的那个;
- 如果两个都不为空,则进行笛卡尔积合并;
两个pattern如何合并的呢?url拼接是由PathMatcher来完成的,我们看下实例
pattern1 | pattern2 | 结果 |
/* |
/hotel | /hotel |
/*.* |
/*.html |
/*.html |
/usr | /user | /usr/user |
/{foo} | /bar | /{foo}/bar |
/hotels/* |
/booking | /hotels/booking |
/hotels/* |
booking | /hotels/booking |
/hotels | /booking | /hotels/booking |
/hotels | booking | /hotels/booking |
/*.html |
/hotel | /hotel.html |
/*.html |
/hotel.* |
/hotel.html |
④ 注册HandlerMethod
从上面看到这里会进行循环然后进行注册,如下所示:
methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); });
AbstractHandlerMethodMapping#registerHandlerMethod
方法如下:
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
继续跟进AbstractHandlerMethodMapping.MappingRegistry#register
:
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); // 这里mapping就是RequestMappingInfo实例 validateMethodMapping(handlerMethod, mapping); // 获取请求URL ,然后往pathLookup放入path与mapping Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping); for (String path : directPaths) { this.pathLookup.add(path, mapping); } // 获取name,然后往nameLookup中放入name=List<HandlerMethod> String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration config = initCorsConfiguration(handler, method, mapping); if (config != null) { config.validateAllowCredentials(); this.corsLookup.put(handlerMethod, config); } this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
代码解释如下:
- ① 获取写锁进行加锁;
- ② 获取HandlerMethod;
③ 校验是否存在不同的HandlerMethod对应同一个RequestMappingInfo实例。简单来说就是同样的@RequestMapping注解在不同的类或者方法上。
④ 获取映射URL,然后放到AbstractHandlerMethodMapping内部类MappingRegistry的private final MultiValueMap<String, T> pathLookup中,path=RequestMappingInfo。这里放的URL是不带通配符*和?的,也就是所谓的directPath。
⑤ 获取handler method的name,然后往nameLookup
中放入name=List<HandlerMethod>
。这里类似于一个彩蛋,如URL/test
访问也可以使用方法TC#test
访问。TC就是TestController
的缩写。
⑥ 判断是否设置了跨域也就是类或者方法上是否有@CrossOrigin注解。如果设置了跨域则初始化CorsConfiguration:
校验是否满足跨域条件:allowCredentials为true时allowedOrigins不能为*,不满足则会抛出异常;
如果满足条件则往corsLookup放入handlerMethod=config:this.corsLookup.put(handlerMethod, config);
⑦ this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));往AbstractHandlerMethodMapping的内部类MappingRegistry的private final Map<T, MappingRegistration<T>> registry = new HashMap<>();放入mapping=new MappingRegistration<>(mapping, handlerMethod, directPaths, name)
- ⑧ 获取写锁,进行锁的释放。
【7】 查找lookupHandlerMethod
AbstractHandlerMethodMapping#lookupHandlerMethod
方法主要是根据lookupPath来获取一个HandlerMethod 。
① lookupHandlerMethod
@Nullable protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); // 从 MappingRegistry.pathLookup中获取 RequestMappingInfo实例集合 List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { addMatchingMappings(this.mappingRegistry.getRegistrations().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.getRegistrations().keySet(), lookupPath, request); } }
mappingRegistry实例图示
方法解释如下:
① 根据lookupPath从MappingRegistry的MultiValueMap<String, T> pathLookup获取List<T> directPathMatches。如果获取到结果不为空,则找出匹配当前请求的RequestMappingInfo实例,根据RequestMappingInfo实例实例和HandlerMethod组装Match,最后放到List<Match> matches中。这里我们可以再回顾一下pathLookup实例:
- ② 如果①操作后的matches为空,则尝试从
MappingRegistry.registry
中获取所有匹配当前请求的数据集,然后封装为Match实例对象放到matches中。
③ 如果② 之后matches不为空,则判断是否只有一条。如果数据集大于一条进行排序,bestMatch = matches.get(0);。这里会额外对第一、二条数据进行比较,如果相等则抛出异常。如果数据集只有一条,则bestMatch = matches.get(0);,继续往下处理。
④ 往request中放入两个属性:
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
⑤ 返回bestMatch中的handlerMethod.
⑥ 如果② 之后matches为空,则走handleNoMatch方法。该方法为空,返回null,在子类RequestMappingInfoHandlerMapping中重写了该方法可能会抛出诸如HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException、UnsatisfiedServletRequestParameterException异常。
request中放的两个属性键是:
// org.springframework.web.servlet.HandlerMapping.bestMatchingHandler String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler"; // org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
上面我们看到了如果matches数据集大于一条会获取比较器然后进行排序,其会调用RequestMappingInfo#compareTo方法进行比较。
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator);
② addMatchingMappings
这里我们再看下addMatchingMappings方法。方法如下所示,会遍历mappings集合(RequestMappingInfo实例集合),从中找到匹配当前request的RequestMappingInfo,然后封装为Match对象放到List<Match> matches中。
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.getRegistrations().get(mapping).getHandlerMethod())); } } }
③ getMatchingMapping&&getMatchingCondition
那么如何判断RequestMappingInfo与request是否匹配呢? 我们继续看下getMatchingMapping
方法。该方法在RequestMappingInfoHandlerMapping
子类中进行了实现。
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) { return info.getMatchingCondition(request); } //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; } PathPatternsRequestCondition pathPatterns = null; if (this.pathPatternsCondition != null) { pathPatterns = this.pathPatternsCondition.getMatchingCondition(request); if (pathPatterns == null) { return null; } } PatternsRequestCondition patterns = null; if (this.patternsCondition != null) { 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, pathPatterns, patterns, methods, params, headers, consumes, produces, custom); }
如上所示,方法会逐个对method、param、header、consume、produce、pathPattern、pattern以及自定义condition进行匹配,只有全部满足则通过新建一个RequestMappingInfo实例返回,否则返回null。
总结
① 在bean实例化时扫描、注册请求URL(或者是RequestMappingInfo实例)与handler;
② 请求处理时,遍历查找。如果不能直接获取或者获取为多个,则会进行比较排序然后选出最优解;
③ 封装为HandlerExecutionChain返回。
【8】几个概念
① RequestMappingInfo
这个类是对请求映射的一个抽象,它包含了请求路径,请求方法,请求头等信息。其实可以看做是@RequestMapping
的一个对应类。其内部维护了静态内部类BuilderConfiguration
、DefaultBuilder
以及一些封装@RequestMapping
注解信息的类。
其内部常量如下所示:
// 一个逻辑分离 (' || ') 请求条件,它根据一组 URL 路径模式匹配请求。这里使用已解析的PathPatterns private static final PathPatternsRequestCondition EMPTY_PATH_PATTERNS = new PathPatternsRequestCondition(); // Patterns 也就是请求URL,PathPatternsRequestCondition 不同,这里使用String 字符串模式 private static final PatternsRequestCondition EMPTY_PATTERNS = new PatternsRequestCondition(); // 请求限制http method private static final RequestMethodsRequestCondition EMPTY_REQUEST_METHODS = new RequestMethodsRequestCondition(); // @RequestMapping注解中的param private static final ParamsRequestCondition EMPTY_PARAMS = new ParamsRequestCondition(); // @RequestMapping注解中的header private static final HeadersRequestCondition EMPTY_HEADERS = new HeadersRequestCondition(); // @RequestMapping注解中的consume private static final ConsumesRequestCondition EMPTY_CONSUMES = new ConsumesRequestCondition(); // @RequestMapping注解中的produce private static final ProducesRequestCondition EMPTY_PRODUCES = new ProducesRequestCondition(); // 自定义条件 private static final RequestConditionHolder EMPTY_CUSTOM = new RequestConditionHolder(null); @Nullable private final String name; @Nullable private final PathPatternsRequestCondition pathPatternsCondition; @Nullable private final PatternsRequestCondition patternsCondition; private final RequestMethodsRequestCondition methodsCondition; private final ParamsRequestCondition paramsCondition; private final HeadersRequestCondition headersCondition; private final ConsumesRequestCondition consumesCondition; private final ProducesRequestCondition producesCondition; private final RequestConditionHolder customConditionHolder; private final int hashCode;
简单实例图示
② HandlerMethod
这个类封装了处理器实例(Controller Bean)和 处理方法实例(Method)以及方法参数数组(MethodParameter[])。其内部成员变量为:
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;
HandlerMethod实例图示
其有用对Method的应用,Method示例图示如下:
③ AbstractHandlerMethodMapping#MappingRegistry
MappingRegistry是AbstractHandlerMethodMapping内部类,内部维护了系列final类型的Map。用来存储"映射与目标"之间的关系,如下所示:
class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>(); private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //... }
其提供了核心方法register
用来向pathLookup、nameLookup、corsLookup以及registry
放入数据。unregister
方法从pathLookup、nameLookup、corsLookup以及registry
移除入数据。
我们可以通过下图直观感受其究竟是一个怎样的实例。
④ ApplicationContextAware与ApplicationObjectSupport
ApplicationContextAware
接口只提供了一个方法用来为当前上下文设置ApplicationContext 。
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
当bean实例化后,会遍历BeanPostProcessor调用其postProcessBeforeInitialization进行后置处理。如ApplicationContextAwareProcessor的postProcessBeforeInitialization会判断bean是否是EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware以及ApplicationStartupAware。如果是其中之一,则会调用对应的setXXX方法如setApplicationContext。
ApplicationObjectSupport
实现了ApplicationContextAware
接口setApplicationContext
方法,如下图所示其提供了initApplicationContext
方法供子类实现。
@Override public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { if (context == null && !isContextRequired()) { // Reset internal context state. this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { // Initialize with passed-in context. if (!requiredContextClass().isInstance(context)) { throw new ApplicationContextException( "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]"); } this.applicationContext = context; this.messageSourceAccessor = new MessageSourceAccessor(context); // 这里是核心 initApplicationContext(context); } else { // Ignore reinitialization if same context passed in. if (this.applicationContext != context) { throw new ApplicationContextException( "Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } }
在本文HandlerMapping体系中,AbstractHandlerMapping、AbstractDetectingUrlHandlerMapping以及SimpleUrlHandlerMapping实现了这三个方法
故而当BeanNameUrlHandlerMapping或者SimpleUrlHandlerMapping实例化时,就会触发setApplicationContext方法,然后触发initApplicationContext方法。