SpringMVC组件之HandlerMapping分析

    xiaoxiao2022-07-07  196

    目录

    1. HandlerMapping概述
    2. HandlerMapping的类图
    3. AbstractHandlerMapping分析
    4. AbstractUrlHandlerMapping族分析
    5. AbstracthandlerMethodMapping族分析
    6. 总结

    1. HandlerMapping概述

    在DispatcherServlet的dodispatch方法中有这段断码:

    // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; }

    在判断完request是不是文件上传请求后,dodispatch调用了getHandler来获取了一个类型为HandlerExecutionChain的对象。而getHandler方法如下:

    /** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or {@code null} if no handler could be found */ @Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { 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; }

    可以看到其遍历了本地的handlerMappings属性,对其中的每一个HandlerMapping调用了getHandler方法,其返回值是一个HandlerExecutionChain对象。 可以看出HandlerExecutionChain其实就是对Handler和HandlerInterceptor的一个包装。 由此我们可以知道HadlerMapping的作用就是根据传入的HttpServletRequest对象找到相应的HandlerExecutionChain,而HandlerExecutionChain就是对Handler和HandlerIntercptor的包装 而关于DispatcherServlet的初始化在之前也有过介绍了,在其onRefresh方法中调用initStrategies,用这个来初始化其九大组件,对于其中的HandlerMapping组件,初始化逻辑是

    根据detectAllHandlerMappings标志为来决定是否在SpringMVC及其父容器中找到所有的类型为HandlerMapping的bean,如果为True的话,则是从容器中获取,并根据@Order进行排序,否则的话就仅从容器中获取beanname为handlerMapping的bean,detectAllHandlerMappings默认为True。如果detectAllHandlerMappings为True而容器中没有类型为HandlerMapping的bean,或者detectAllHandlerMappings为False但容器中没有beanname为handlerMapping的bean的话,就根据DispatcherServlet.properties这个文件中配置的HandlerMapping类名来初始化HandlerMapping. 很据上面这个逻辑的话,那我们一般配置SpringMVC的时候没有主动的配置HandlerMapping,那应该就是采用的DispatcherServlet.properties这个文件里配置的类作为HandlerMapping了哦。但其实我们一般配置的时候会有一个或者使用@EnableWebMVC,这个标签或者注解其实帮助我们做了一些事情的,比如采用XML配置的话会有: public class MvcNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser()); } }

    可以看到当配置了annotation-driven后,MvcNamespaceHandler会使用AnnotationDrivenBeanDefinitionParser对XML文件进行解析,而这个Parser有这样的代码:

    public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName(); public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();

    也就是说当配置了mvc:annotation-driven时,框架会自动的将HandlerMapping设置为RequestMappingHandlerMapping,将HandlerAdapter设置为RequestMappingHandlerAdapter。

    2. HandlerMapping的类图

    从这个体系结构我们可以看出,HandlerMapping主要可以分为两类,一类继承自AbstractUrlHandlerMapping,一类继承自AbstractHandlerMethodMapping,这个两个类又都继承了AbstractHandlerMapping,实现了MatchableHandlerMapping。 HandlerMapping是一个函数接口,只有一个方法

    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

    3. AbstractHandlerMapping分析

    可以看到AnstractHandlerMapping继承了WebApplicationObjectSupport,所以其初始化的时候会调用模版方法initApplicationContext.

    ... private final List<Object> interceptors = new ArrayList<>(); private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>(); ... protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }

    其中extendInterceptors是个模版方法,默认实现为空。子类可以覆盖这个方法,用以在已有拦截器之前和之后注册额外的拦截器。 detectMappedInterceptors将容器中所有类型为MapperIntercepter类型的bean添加到了adaptedInterceptors属性中

    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( obtainApplicationContext(), MappedInterceptor.class, true, false).values()); }

    initIntercptors的函数内容为:

    protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } } protected HandlerInterceptor adaptInterceptor(Object interceptor) { if (interceptor instanceof HandlerInterceptor) { return (HandlerInterceptor) interceptor; } else if (interceptor instanceof WebRequestInterceptor) { //使用适配器模式将WebRequestInterceptor适配为HandlerInterceptor return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); } else { throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); } }

    对于interceptors这个属性来说,有两种设置方法,一种是通过HandlerMapping的setInterceptors方法设置,一种是通过子类实现extendInterceptors钩子方法进行设置。 方法detectMappedInterceptor将容器中类型为MapperInterceptor类型的bean查找出来,在这里将查找到的MapperInterceptor存储到了adaptedInterceptors这个属性中了,MapperInterceptor是HandlerInterceptor的子类,用了装饰者模式,除了preHandle。postHandle和afterCompletion之外还提供了match方法,也就是说MapperInterceptor的是由作用范围的,会根据它的includePatterns,excludePatterns来决定是否拦截某个请求路径。 方法initInterceptors,会遍历interceptors属性,先判断其是否为null,是的话就报错,否者 调用adaptInterceptor方法,将其添加到adaptInterceptor属性中。 从以上代码中我们可以得出结论,AbstractHandlerMapping会引用到容器中所有的interceptor,并且将其存储在其adaptedInterceptors属性中。接下来看看其主要方法,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 = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; } 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; }

    getHandler首先调用了其模版方法getHandlerInternal,交由子类去找handler,然后将子类返回的handler做个判断,如果为空则使用默认的handler,如果默认的handler也为空的话就直接返回null。 得到不为空的handler就判断其是否是string类型,是的话就从容器中获取这个name代表的Bean。 AbstractHandlerMapping有了handler,还有所有的interceptor,现在需要做的就是将handler和需要配置的interceptor一起封装起来形成HandlerExecutionChain,所以其调用了getHandlerExecutionChain,根据request找到了需要的interceptor,这里的interceptor分分为了两类,一类是MappedInterceptor,需要选择性的配置,一类是非MappedInterceptor,这种interceptor会拦截所有的request。 **可以看出,AbstractHandlerMapping拥有容器中所有的interceptor,存储在其adaptedInterceptors属性中。然后交由子类通过getHandlerInternal方法找到request对应的Handler,最后遍历adaptedInterceptors属性找到需要配置的interceptor,最后封装成HandlerExecutionChain对象返回给DispatcherServlet。**在源码中还可以看出AbstractHandlerMapping还和跨域请求有关,这部分就在之后进行分析了。

    4. AbstractUrlHandlerMapping族分析

    从上面的分析,我们知道了AbstractHandlerMapping的子类需要做的就是实现getHandlerInternal,即通过某种方式去找到request相对应的handler。AbstractUrlHandlerMapping从名字上就可以看出它是通过request的url进行匹配的。这个类簇的大致原理是将url和handler的对应关系用map保存起来,在getHandlerInternal方法中使用url从Map中获取其对应的Handler。AbstractUrlHandlerMapping实现了用url在Map中查找Handler的过程,而Map的初试化则交给了具体的子类去完成。

    protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); Object 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 ("/".equals(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); } } if (handler != null && logger.isDebugEnabled()) { logger.debug("Mapping [" + lookupPath + "] to " + handler); } else if (handler == null && logger.isTraceEnabled()) { logger.trace("No handler mapping found for [" + lookupPath + "]"); } return handler; }

    即通过lookupHandler方法去找到handler, 如果没有找到,即handler为null:     则判断路径是否为“/”,是的话将rootHandler设置给rawHandler(rootHandler是AbstractUrlHandlerMapping的一个属性,用于处理路径为“/”的请求) 如果路径不是"/",或者rootHandler没有设置,则将rawHandler设置为defaultHandler。如果rawHandler不为空,则看rawHandler是否为String类型,如果是String类型,则从容器中获取对应的Bean,然后调用了buildPathExposingHandler给rawHandler添加两个内部拦截器。 从这个过程可以看出,主要的方法就两个lookupHandler与buildPathExposingHandler。

    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // Direct match? //先看哈是否能够直接匹配上 Object handler = this.handlerMap.get(urlPath); if (handler != null) {//可以直接通过url找到handler // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } validateHandler(handler, request); return buildPathExposingHandler(handler, urlPath, urlPath, null); } // Pattern match? //临时变量,因为一个url可能与多个pattern相匹配 List<String> matchingPatterns = new ArrayList<>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { matchingPatterns.add(registeredPattern); } //useTrailingSlashMatch标志代表/AA/等于/AA else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) { matchingPatterns.add(registeredPattern +"/"); } } }//到这里已经找到了与request的url相匹配的所有pattren。 String bestMatch = null; Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { matchingPatterns.sort(patternComparator); if (logger.isDebugEnabled()) { logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns); } //找到与url最优匹配的pattern bestMatch = matchingPatterns.get(0); } if (bestMatch != null) { //找到bestMatch相对应的handler 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, urlPath); // 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<>(); //有可能多个Pattren的顺序相同,再次比较下 for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } if (logger.isDebugEnabled()) { logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables); } return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }

    虽然说有一个用于request与handler对应的Map,但是大部分情况下也不能直接通过url找到handler。因为有的handler使用了pattern的匹配模式。

    buildPathExposingHandler方法用于给查找到的Handler注册两个拦截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor。

    protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) { HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler); //添加拦截器 chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping)); if (!CollectionUtils.isEmpty(uriTemplateVariables)) { chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables)); } return chain; } private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter { private final String bestMatchingPattern; private final String pathWithinMapping; public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) { this.bestMatchingPattern = bestMatchingPattern; this.pathWithinMapping = pathWithinMapping; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request); request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings()); return true; } } protected void exposePathWithinMapping(String bestMatchingPattern, String pathWithinMapping, HttpServletRequest request) { request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern); request.setAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping); } private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter { private final Map<String, String> uriTemplateVariables; public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) { this.uriTemplateVariables = uriTemplateVariables; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { exposeUriTemplateVariables(this.uriTemplateVariables, request); return true; } } protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) { request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables); }

    在这个地方我们可以知道AbstractUrlHandlerMapping的getHandlerInternal会调用lookupHandler,而lookupHandler会从handlerMap中找到与url相匹配的handler,而这个找的过程又可以分为直接查找与通过pattern模式匹配查找,如果能找到的话会调用buildPathExposingHandler方法将一些属性设置到request域中。 在这个地方其实也就是一个查找的过程,那我们再去看看查找所需要的handlerMap是怎样建立的。

    protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { Assert.notNull(urlPaths, "URL path array must not be null"); for (String urlPath : urlPaths) { registerHandler(urlPath, beanName); } } 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. //如果handler是String类型的且没有设置lazyInitHandlers,则从SpringMVC容器中获取handler. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } //从handlerMap中看这个url是否已经注册过handler了,可以多个url对应一个handler,但不能说一个完全一模一样的url对 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 { //如果路径等于/则设置为rootHandler if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } //如果路径等于"/*"则设置defaultHandler else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }

    可以看到有两个registerHandler,第一个的参数为String[]和String可以通过这个方法来将多个pattern或者url注册到一个handler上(这里的handler是一个容器中的bean),它会调用第二个方法。第二个则是会先从容器中检查这个url是否已经组册过了,不允许一个url(注意是调用的get方法)对应多个handler。然后还会根据路径是‘/’或者‘/*’来设置rootHandler和defaultHandler。 现在再来看看AbstractUrlHandlerMapping的子类,SimpleUrlHandlerMapping,

    @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); }

    SimpleUrlHandlerMapping重写了父类的initApplicationContext,除了直接调用父类的初试方法之外,还调用了自己的registerHandlers方法:

    protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } 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); }); } }

    可以看出SimpleUrlHandlerMapping类就是直接将自身的urlMap内容注册到handlerMap中。 另一个继承自AbstractUrlHandlerMapping的就是AbstractDetecingUrlHandlerMapping,这个类也是非常简单的

    public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping { private boolean detectHandlersInAncestorContexts = false; /** * Set whether to detect handler beans in ancestor ApplicationContexts. * <p>Default is "false": Only handler beans in the current ApplicationContext * will be detected, i.e. only in the context that this HandlerMapping itself * is defined in (typically the current DispatcherServlet's context). * <p>Switch this flag on to detect handler beans in ancestor contexts * (typically the Spring root WebApplicationContext) as well. */ public void setDetectHandlersInAncestorContexts(boolean detectHandlersInAncestorContexts) { this.detectHandlersInAncestorContexts = detectHandlersInAncestorContexts; } /** * Calls the {@link #detectHandlers()} method in addition to the * superclass's initialization. */ //AbstractDetecingHandlerMapping也是通过重写父类的initApplicationContext来设置handlerMap的 @Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext(); detectHandlers(); } /** * Register all handlers found in the current ApplicationContext. * <p>The actual URL determination for a handler is up to the concrete * {@link #determineUrlsForHandler(String)} implementation. A bean for * which no such URLs could be determined is simply not considered a handler. * @throws org.springframework.beans.BeansException if the handler couldn't be registered * @see #determineUrlsForHandler(String) */ protected void detectHandlers() throws BeansException { ApplicationContext applicationContext = obtainApplicationContext(); if (logger.isDebugEnabled()) { logger.debug("Looking for URL mappings in application context: " + applicationContext); } //在容器中找到所有的bean的name 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) { //这是个模版方法,通过bean的name得出url路径 String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { // URL paths found: Let's consider it a handler. //得到对应关系的话就组册到handlerMap中 registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } } /** * Determine the URLs for the given handler bean. * @param beanName the name of the candidate bean * @return the URLs determined for the bean, or an empty array if none */ protected abstract String[] determineUrlsForHandler(String beanName); }

    而AbstractDetectingUrlHandlerMapping有子类,BeanNameUrlHandlerMapping,而这个HandlerMapping的源码

    public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { /** * Checks name and aliases of the given bean for URLs, starting with "/". */ @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和alias是不是以"/"开头,如果是的话就将其作为url。 至此AbstractUrlHandlerMapping的分析就完毕了,可以看出它的层次结构也是很清晰的,在AbstractUrlHandlerMapping中实现了在handlerMap中通过url查找Handler的逻辑,而handlerMap的初始化则是交给子类来处理的,这里初始化又分为两种,一种是直接给出对应关系,另一种则是在容器中查找beanName或者alias以”/“开头的。

    5. AbstracthandlerMethodMapping族分析

    AbstractHandlerMethodMapping系列是将Method作为Handler来使用的,这也是平时使用最多的一种Handler–HandlerMethod,例如@RequestMapping所注释的方法就是这种handler. HandlerMethod的主要继承结构是HandlerMethod–>InvocableHandlerMethod–>ServletInvocableHandlerMethod。 HandlerMethod的主要属性为: Object bean; //用于封转bean Method method; //用于具体处理请求的method BeanFactory beanfactory; //主要用于新建HandlerMethod时传入的Handler时String类型的情况,这时要从容器中根据beanName获取到真实的Bean,并将其设置为Handler Method bridgedMethod; //如果method是bridge method则设置为其所对应的原有方法,否则直接设置为method.这个与泛型有关 Methodparameter[] parameters;//代表处理请求的方法。 关于这个类族,在之后的HandlerAdapter中我们会再次的详细的去看看的。现在就先回到AbstractHandlerMethodMapping中, AbstractHandlerMethodMapping还实现了InitializingBean,这是bean生命周期中的一个方法,会在参数设置完成后调用其afterPropertiesSet方法,这个方法的内容为:

    @Override public void afterPropertiesSet() { initHandlerMethods(); } protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } //从容器中获取所有的beanNames String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { 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.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } //通过isHandler来筛选Handler 这是个模版方法,在RequestMappingHandlerMapping里面实现了这个方法,逻辑就是判断类是否有@Controller或者@RequestMapping注释 if (beanType != null && isHandler(beanType)) { //detectHandlerMethods负责将Handler保存到Map中, detectHandlerMethods(beanName); } } } //可以对Handler进行一些初试化工作 handlerMethodsInitialized(getHandlerMethods()); } protected void detectHandlerMethods(final Object handler) { //获取实际Handler的类型 Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { final Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { //这是个模版方法,在RequestMappingHandlerMapping中实现,将@RequestMapping注解的信息转换为RequestMappingInfo return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isDebugEnabled()) { logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }

    到现在我们可以清楚AbstractHandlerMethodMapping的初试化过程,总结起来就是查找容器中所有的beanName,然后遍历这些beanName,对每一个beanName取得其类类型,看它是否是有@Controller或者@RequestMapping注解(这是在RequestMappingHandlerMapping中完成的),如果有的话会调用detectHandlerMethods,这个方法会遍历传入Bean的所有符合条件的方法,即可以理解为有@RequestMapping注解的方法。将@RequestMapping中的信息转换为RequestMappingInfo对象,就形成了method与RequestMappingInfo对应关系的map,最后再将这个Map存储在mappingRegistry属性中.这是AbstractHandlerMethodMapping的内部类MappingRegistry。这个内部类主要有以下属性

    private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); private final MultiValueMap<String, T> urlLookup = 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();

    看了其初始化过程,再看下其是如何运作的。从之前的分析中知道AbstractHandlerMapping会调用子类的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 + "]"); } } //createWithResolvedBean是用于处理handlerMethod是String类型的情况 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }

    现在看这个方法就能看的很清楚了,它就是通过request的lookUpPath找到Handler,即HandlerMethod对象。如果找到的话就调用createWithResolvedBean方法后返回。在这个过程中还用了一个读写锁来保护整个方法,这是因为前面说的MappingRegistry中有线程不安全的集合类。

    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { //Match是个内部类,封装了匹配条件和HandlerMethod两个属性 List<Match> matches = new ArrayList<>(); //根据lookupPath直接在mappingRegistry的urlLookup属性中查找T,这个T是类类型,默认是RequestMappingInfo List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { //最终调用了RequestMappingInfo的getMatchingCondition方法 addMatchingMappings(directPathMatches, matches, request); } //如果不能根据lookupPath找到RequestMappingInfo对象,则将所有的RequestMappingInfo都遍历一遍,看是否和request相匹配 if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } //如果有与request相匹配的RequestMappingInfo if (!matches.isEmpty()) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(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); //返回找到的handlerMethod return bestMatch.handlerMethod; } else { //返回null return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }

    6. 总结

    在这篇笔记中对HandlerMapping类族做了个简单的分析,可以清楚的是:HandlerMapping就只定义一个方法,getHandler,作用是根据request找到相应的HandlerExecutionChain。HandlerExecutionChain就是对Interceptor和handler的包装。而Handler的定义是丰富多样的,可能是某个类也可能是某个方法,所以后面流程需要交给HandlerMapping处理。而HandlerMapping有个抽象类AbstractHandlerMapping,这个类拥有容器中所有的interceptor,所以关于interceptor的处理就交给了AbstractHandlerMapping,而找Handler的方法就交给了子类去完成,所以子类需要实现模版方法getHandlerInternal。而从request找handler又分为了两大类,一是根据url路径来查找,即AbstractUrlHandlerMapping类族,它又分为SimpleUrlHandlerMapping,它的handlerMap是我们直接告诉容器的,而AbstractDetctingHandlerMapping则是从容器中获取url与bean的对应关系,它的子类BeanNameHandlerMapping就是看beanName或者alias是否以’/'开始。二是根据RequestMappingInfo来查找Handler(即AbstractHandlerMethodMapping),这里的Hanndler就是HandlerMethod的类型。它的基本思路就是查找到容器中所有的以@Controller或者@RequestMapping注解的bean,然后遍历bean中的方法,看方法是否被@RequestMapping注释,有的话就将@RequestMapping中的信息转化为RequestMappingInfo,这是RequestCondition的子类。将RequestMappingInfo和HandlerMethod的对应关系放到Map中,然后将这些信息放到AbstractHandlerMethodMapping的MappingRegistry属性中,以供之后查找用。

    最新回复(0)