SpringMVC源码分析----HandlerMapping器用分析法

如下HandlerMapping的继承结构如下,主要分为两支,一支继承AbstractUrlHandlerMapping,一支继承AbstractHandlerMethodMapping。

一、AbstractHandlerMapping

         先介绍AbstractHandlerMapping,这是HandlerMapping的抽象实现。getHandler方法实现接口方法,并提供模板方法getHandlerInternal留给子类实现,这个抽象类保存了配置的Interceptor,在获取到Handler后会根据request提取的lookupPath将相应的Interceptor装配上去,当然也可以通过getHandlerInternal方法设置自己的Interceptor

    说明:Interceptor:用于设置SpringMVC的拦截器,两种设置方式:1)通过注册HandleMapping时属性设置;2)通过子类的extendInterceptors钩子方法设置。其实是通过initInterceptor方法把所有都添加进了adaptedInterceptors中。

另外后面MappedInterceptors是一个单独的类(Spring之前是设置的仅仅一个和adaptedInterceptors类似的List属性),这个是使用时需要和url匹配成功,单独添加到HandlerExecutionChain。

  1.AbstractHandlerMapping之器:

           AbstractHandlerMapping继承WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext,而AbstractHandlerMapping的Interceptor创建就是在这个方法里面实现的。如下:

    protected void initApplicationContext() throws BeansException {
        //是模板方法,用于给子类提供了一个添加Interceptors的入口,不过这个在SpringMVC中并没有使用
        this.extendInterceptors(this.interceptors);
       //用于将SpringMVC容器和父容器的所有Interceptor添加到adaptedInterceptors中
        this.detectMappedInterceptors(this.adaptedInterceptors);
         //这个是初始化Interceptor,具体内容是将所有的interceptort添加到adaptedInterceptors。
        this.initInterceptors();
    }
    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(this.adaptInterceptor(interceptor));
            }
        }

    }

    2.AbstractHandlerMapping之用:

            通过getHandler方法实现handler的获取HandlerExecutionChain,源码:  

 @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = this.getHandlerInternal(request); //模板方法由子类实现
        if (handler == null) {
            handler = this.getDefaultHandler();//如果没有实现采用默认的
        }

        if (handler == null) {
            return null;
        } else {
            if (handler instanceof String) { //如果为String则从容器中查找bean
                String handlerName = (String)handler;
                handler = this.obtainApplicationContext().getBean(handlerName);
            }

            HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
            if (CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
                CorsConfiguration config = globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig;
                executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
            }

            return executionChain;
        }
    }

    @Nullable
    protected abstract Object getHandlerInternal(HttpServletRequest var1) throws Exception;

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        Iterator var5 = this.adaptedInterceptors.iterator();

        while(var5.hasNext()) {
            HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
            //通过判断是否属于MappedInterceptor把它转换为MappedInterceptor添加进入chain
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            } else {
                //否则也会添加进入chain
                chain.addInterceptor(interceptor);
            }
        }

        return chain;
    }

  Object handler = this.getHandlerInternal(request); //模板方法由子类实现.

二、AbstractUrlHandlerMapping

           原理:通过url来匹配,就是讲url和对应的Handler保存在一个Map中,在getHandlerInternal方法中使用url从Map中获取Handler。这个分支开发用的较少,这里不做分析。

三、AbstractHandlerMethodMapping

           这个系列的结构脂粉简单只有三个类,AbstractHandlerMethodMapping、RequestMappingInfoHandlerMapping和RquestMappingHandlerMapping,这三个类依次继承,这条分支是将Method作为Handler来使用,也是我们用的最多的一种Handler,比如常用的@RequestMapping注释的方法就是这种Handler,他专门有一个类型HandlerMethod,也就是Method类型。

    1. AbstractHandlerMethodMapping之器

         1.1   明白三个Map(mappingLookup,urlLookup,nameLookup)的含义,在这个AbstractHandlerMethodMapping有一个内部类MappingRegistry,它里面有三个Map,如下:

class MappingRegistry {
        private final Map<T, AbstractHandlerMethodMapping.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();
}

    上面的泛型T也是不仅仅只url,还有很多其他条件,如request的类型(get,post)、请求的参数、Header等都可以作为匹配HanderMethod的条件,默认使用的是RequestMappingInfo,通过RequestMappingInfoHanderMapping的定义可以看出来:

public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {...}

 知识补充:  RequestCondition接口

          RequestMappingInfo接口实现类RequestCondition接口,此接口专门用于保存从request提取出的用于匹配Handler的条件。

  •     抽象实现AbstractRequestCondition中重写了equals、hashcode和toString三个方法,还有8个子类,除了CompositeRequestCondition外每一个子类表示一种匹配条件。如:PatternsRequestConditon使用url做匹配,RequestMethod是RequestCondition用RequestMethod做匹配。CompositeRequestConditon本身不做匹配,而是将多个别的Condition封装到自己的一个变量中,再用的时候遍历封装RequestCondition的那个变量的所有RequestConditon进行匹配,也就是大家说的责任链默认,这种模式一般用CompositeXXX或者XXXComposite命名。
  •         另一个实现RequestMappingInfo,它的里面使用七个变量保存了前面的七个RequestConditon的实现类,在使用时用这个七个变量进行匹配,也就是在@RequestMapping中给处理器指定多种匹配方式的原因。

部分源码:

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
    @Nullable
    private final String name;
    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;
...
}

下面详细讲解三个map的含义

  •       第一个map--mappingLookup:保存着匹配条件(RequestConditon)和HandlerMethod的关系。
  •      第二个map--urlLookup:保存着url和匹配条件(RequestConditon)的关系,这里的url也可以是pattern模式的,可以使用通配符,这里的map的value可以是对应多个值的,value是一个List类型,看如下定义可知。
public interface MultiValueMap<K, V> extends Map<K, List<V>> {}

 

由于RequestCondition可以同时使用多种不同的匹配方式而不是url一种,所以反过来说同一个url就可以能有多个RequestConditon与之相对应。这里的RequestConditon就是@RequestMapping中注释的内容。

  •     第三个map----nameLookup是 SpringMVC后面新增的,他直接保存着name和HandlerMethod的关系,一个name对应着多个HandlerMethod,如下:
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap();

这里的name是通过handleMethodMappingNameStrategy策略的实现类从HandlerMethod中解析出来的,默认使用的是RequestMappingInfoHandlerMethodMappingNamingStrategy实现类,解析规则是:类名的大写字母组合+“#”+方法名。这个Map在正常的匹配中并不需要使用。它主要使用在MvcUriComponentsBuilder里面,可以通过name获取相应的url,比如:

MvcUriComponentBuilder.MethodArgumentBuilder uriComponent = MvcUriComponentBuilder.fromMappingName("GC#index");
String uri = uriComponent.build();

这样就可以构造出url----http://localhost:8080/index,也即使前面GoController(这个是通过实现Controller接口做的)的index方法对应的url(GC是GoController手写字母大写)。另外在jsp通过Spring的标签来使用,这样就可以访问到http://localhost:8080/index,网址了。

<a href="${s:mvcUrl ('GC#index')}" > Go </a>

总结:

    urlLookup 保存着url和RequestConditon的关系,通过url拿到匹配的条件,然后再由mappingLookup通过匹配条件获取到对应的HandlerMethod。当前url可能对应多个RequestCondition,所以这里需要选出一个最优的ReqeustCondition。

1.2  容器初始化afterPropertiesSet方法

      由于AbstractHandlerMethodMapping实现了InitializingBean接口,所以spring容器会自动调用afterPropertiesSet,这个方法又交给initHandlerMethods方法去实现。关键代码处有注释,代码如下:

     public void afterPropertiesSet() {
        this.initHandlerMethods();
    }

    protected void initHandlerMethods() {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Looking for request mappings in application context: " + this.getApplicationContext());
        }
        //从容器中获取所有的bean
        String[] beanNames = this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.obtainApplicationContext(), Object.class) : this.obtainApplicationContext().getBeanNamesForType(Object.class);
        String[] var2 = beanNames;
        int var3 = beanNames.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String beanName = var2[var4];
            if (!beanName.startsWith("scopedTarget.")) {
                Class beanType = null;

                try {
                    beanType = this.obtainApplicationContext().getType(beanName);
                } catch (Throwable var8) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Could not resolve target class for bean with name '" + beanName + "'", var8);
                    }
                }

                if (beanType != null && this.isHandler(beanType)) { //过滤hander,isHandler是通过使用有@Controller或者@RequestMapping注释作为判断是否是Handler,由子类实现
                    this.detectHandlerMethods(beanName); //负责将Handler保存到Map中
                }
            }
        }

        this.handlerMethodsInitialized(this.getHandlerMethods());//可以对Handler进行一些初始化,模板方法不过子类并没有实现。
    }

接着看detectHanderMethods方法的具体实现,怎么讲Handler保存到Map当中。

   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.isDebugEnabled()) {
                this.logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
            }

            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                this.registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }

    }

2. AbstractHandlerMethodMapping之用

   通过AbstractHandlerMapping分析留给子类一个模板方法getHandlerInternal方法,这个方法在AbstractHandlerMethodMapping子类中是这样实现的,重要方法有注释:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //通过request获取url
        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Looking up handler method for path " + lookupPath);
        }

        this.mappingRegistry.acquireReadLock();

        HandlerMethod var4;
        try {
            //通过url和request获取到handlerMethod
            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
            if (this.logger.isDebugEnabled()) {
                if (handlerMethod != null) {
                    this.logger.debug("Returning handler method [" + handlerMethod + "]");
                } else {
                    this.logger.debug("Did not find handler method for [" + lookupPath + "]");
                }
            }
            //通过handlerMethod创建新的handlerMethod返回,判断是否为handler是否是String,如果是的话需要重新从容器中获取。
            var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
        } finally {
            this.mappingRegistry.releaseReadLock();
        }

        return var4;
    }

下面看怎么通过url和request获取到HanderMethod的方法lookupHandlerMethod源码,标注有重要注释:

   protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        //这里的Match是内部类,保存匹配条件和Handler
        List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            //将找到的匹配条件添加到match中
            this.addMatchingMappings(directPathMatches, matches, request);
        }

        if (matches.isEmpty()) {
            //如果不能通过url找到匹配条件,这将所有的匹配条件添加到matches中
            this.addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            //将包含匹配条件和Handler的matches排序,并争取第一个作为bestMatch,如果前两个出现相同则抛出异常
            Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
            matches.sort(comparator);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
            }

            AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            if (matches.size() > 1) {
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }

                AbstractHandlerMethodMapping<T>.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)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 + "}");
                }
            }

            this.handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        } else {
            return this.handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

四、总结

      整个HandlerMapping整体结构是在AbstractHandlerMapping中设计的,简单的功能就是request找到Handle和Interceptors,组合层HandlerExecutionChain类型返回,找到Handler的过程通过模板方法getHanderMappng留给子类去实现,而查找Interceptors则是AbstractHandlerMapping自己完成的。查找的具体方法是通过几个与Interceptor相关的Map完成的,在初始化中对那些Maping进行初始化。

     AbstractHandlerMapping的子类分为两个系列:AbstractUrlHandlerMapping系列和AbstractHandermethodMapping系列,前者通过url匹配,后者匹配的内容很多,而且都是通过匹配Method,这也是现在用的最多的一种方式。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值