享读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. 总结


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

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


相关文章
|
3月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
43 0
|
3月前
|
Java 应用服务中间件 数据库连接
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
38 0
|
10月前
|
存储 前端开发 搜索推荐
(八)Spring源码解析:Spring MVC
(八)Spring源码解析:Spring MVC
56 1
|
5月前
|
设计模式 前端开发 Java
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
|
5月前
|
SQL JSON 前端开发
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
65 0
|
前端开发 Java Spring
源码浅析SpringMVC请求的流转过程
Spring MVC框架使用了其”模型-视图-控制器”( Model-View-Controller )架构方式,用于开发灵活且松散耦合的 Web 应用程序。我们都使用过SpringMVC来处理信息,并渲染视图到Browser。但需要注意的是,在现在的架构中,大都采用了前后端分离的情况,而我们在使用SpringMVC的时候,只需要关注M(Model),C(Controller)这两个部分,而视图渲染的部分则交给了前端。
286 0
源码浅析SpringMVC请求的流转过程
|
JSON 缓存 Java
享读SpringMVC源码5-异常处理HandlerExceptionResolver(下)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(下)
|
Java Spring 容器
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
|
Java Spring
享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(下)
享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(下)
享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(下)
|
数据采集 缓存 Java
享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(上)
享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(上)