享读SpringMVC源码2-@RequestMapping注解源码(下)

简介: 享读SpringMVC源码2-@RequestMapping注解源码(下)
3.2.2 登记(缓存):

登记动作发生在registerHandlerMethod 方法中。此时就是把各种关系缓存起来。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    protected void detectHandlerMethods(final Object handler) {
            Class<?> handlerType = (handler instanceof String ?
                    getApplicationContext().getType((String) handler) : handler.getClass());
            final Class<?> userType = ClassUtils.getUserClass(handlerType);
            //找:方法搜索器,搜索当前bean继承体系上的所有方法中,符合我们需要的method
            //....
            //注册,
            for (Map.Entry<Method, T> entry : methods.entrySet()) {
                //找到可调用的方法
                Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
                T mapping = entry.getValue();
                //注册
                registerHandlerMethod(handler, invocableMethod, mapping);
            }}
    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
            this.mappingRegistry.register(mapping, handler, method);
        }
    class MappingRegistry {
        //三个参数直接信息, handler类,hangler方法
        public void register(T mapping, Object handler, Method method) {
                    this.readWriteLock.writeLock().lock();//线程安全
                    try {
                        //创建一个HandlerMethod
                        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                        //检查路径是否被定义过,确保一个路径只能找到一个method(路径定义重复的错误就是在这里抛出的)
                        assertUniqueMethodMapping(handlerMethod, mapping);
                        //日志,启动时可以看到
                        if (logger.isInfoEnabled()) {
                            logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
                        }
                        // mappingLookup: 缓存Mapping和handlerMethod的关系  
                        this.mappingLookup.put(mapping, handlerMethod);
                        //获取注解里的路径信息。
                        List<String> directUrls = getDirectUrls(mapping);
                        //缓存路径与mapping的关系
                        for (String url : directUrls) {
                            this.urlLookup.add(url, mapping);
                        }
                        // 保存name和handlerMethod的关系  同样也是一对多
                        String name = null;
                        if (getNamingStrategy() != null) {
                            name = getNamingStrategy().getName(handlerMethod, mapping);
                            addMappingName(name, handlerMethod);
                        }
                        //初始化跨域配置,并缓存handlerMethod与跨域配置的
                        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                        if (corsConfig != null) {
                            this.corsLookup.put(handlerMethod, corsConfig);
                        }
                        // 注册mapping和MappingRegistration的关系                
                        this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
                    }
                    finally {
                        this.readWriteLock.writeLock().unlock();
                    }
                }
    }
}

总结注册过程做了啥:

  • 根据handler,method创建一个HandlerMethod实例
  • 缓存mapping和handlerMethod的关系(mappingLookup)
  • 缓存路径url与mapping的关系()
  • 缓存name和handlerMethod的关系
  • 根据mapping,handlerMethod,urls,name创建一个MappingRegistration相关实例
  • 缓存mapping和MappingRegistration的关系

Bean初始化完成后,我们定义的接口方法也就以各种形态,各种关系缓存起来了。


4. 获取handler


当请求来到DispatcherServlet#doDispatch 方法时。遍历所有的handlerMapping, 并尝试从其实例中获取Handler, 如果获取到就返回。

mappedHandler = getHandler(processedRequest);
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;
    }

对于我们以@RequestMapping("/xxxx")注解定义的handler。RequestMappingHandlerMapping的父类AbstractHandlerMapping 提供对getHandler的实现。

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
   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.globalCorsConfigSource.getCorsConfiguration(request);
               CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
               CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
               executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
           }
           return executionChain;
       } 
}

上一篇文章说过这里是个模板方法。定义了算法骨架:

  • 找handler
  • 包装


4.1 匹配

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //获取请求的URI 的path
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        this.mappingRegistry.acquireReadLock();
        try {
            //根据url从多个缓存关系中选择HandlerMethod
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }


lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<Match>();
        //根据url从关系缓存:urlLookup获取对应的匹配条件集
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        //如果没有找到匹配条件,从整个关系缓存mappingLookup中寻找
        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 + "}");
                }
            }
            //找到匹配后,在Attributes中加入一些相关信息。
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            //返回最优匹配的handlerMehod
            return bestMatch.handlerMethod;
        }
        else {
            //没有找到。交给子类去处理没有找到的情况
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }


4.2 包装

找到handler后,并不是直接返回handler . 为了 体现框架设计的开闭原则,SpringMVC把拦截与Handler 包装成一个HandlerExecutionChain .

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;


5. 总结


因为篇幅的原因,很多东西看源码理解的更快,本文提供观看思路,加入个人的理解。

框架千千万万,我们不需要把每一个细节看到非常透,抓住主干,理解原理。我们已经成功了一步。


相关文章
|
JSON 前端开发 Java
Spring MVC入门必读:注解、参数传递、返回值和页面跳转(下)
Spring MVC入门必读:注解、参数传递、返回值和页面跳转(下)
109 0
|
6月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
76 0
|
3月前
|
前端开发 Java Spring
SpringMVC种通过追踪源码查看是哪种类型的视图渲染器(一般流程方法)
这篇文章通过示例代码展示了如何在Spring MVC中编写和注册拦截器,以及如何在拦截器的不同阶段添加业务逻辑。
SpringMVC种通过追踪源码查看是哪种类型的视图渲染器(一般流程方法)
SpringMVC入门到实战------3、@RequestMapping注解(超详细基础知识+实际代码案例)
该博客文章详细介绍了SpringMVC中`@RequestMapping`注解的使用方法,包括其功能、位置、value属性、method属性、params属性、headers属性以及支持的路径风格和占位符,并通过实际代码案例展示了如何建立请求与控制器方法之间的映射关系。
SpringMVC入门到实战------3、@RequestMapping注解(超详细基础知识+实际代码案例)
|
6月前
|
前端开发 Java Spring
请求映射掌握:探讨Spring MVC中@RequestMapping注解的妙用
请求映射掌握:探讨Spring MVC中@RequestMapping注解的妙用
148 1
请求映射掌握:探讨Spring MVC中@RequestMapping注解的妙用
|
缓存 前端开发 Java
【SpringMVC】JSR 303与拦截器注解使用
【SpringMVC】JSR 303与拦截器注解使用
73 0
|
6月前
|
Java 应用服务中间件 数据库连接
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
65 0
|
6月前
|
SQL JSON 前端开发
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
106 0
|
6月前
|
XML 前端开发 Java
【SpringMVC】 一文掌握 》》》 @RequestMapping注解
【SpringMVC】 一文掌握 》》》 @RequestMapping注解
67 1
|
6月前
|
前端开发 Java 应用服务中间件
SpringMVC之@RequestMapping注解
SpringMVC之@RequestMapping注解