HandlerMapping源码分析与应用(获取所有URI)

star2017 1年前 ⋅ 318 阅读

整理这篇文章由两个因素引发:

  • 一个是参与的微服务项目涉及到权限管理应用到了 HandlerMapping ,使用自定义注解,自动或手动拿到各个微服务所有自定义的 Controller RequestMapping(URI)同步到权限管理模块。
  • 二是开发的项目报了异常,打印的异常信息比较有意思,就跟踪源码查到信息也是来自于 HandlerMapping,这些信息可以自己获取后应用在拦截器或 Controller 层的 AOP 中。

备注:已使用 AOP 拿到类似到源码抛出的打印的异常信息,但没有源码抛出的异常信息完美优雅。

HandlerMapping 是 Spirng MVC 的执行流程中需要用到的一个组件,用于获取 Handler 配置的所有相关的对象,包括 Handler 对象对应的拦截器,把这些对象封装到 HandlerExecutionChain(处理执行器链) 对象当中返回。可参考 Spring MVC 的执行流程

HandlerMapping 有多个实现,可看下面的类图,而 RequestMappingHandlerMapping 是用得比较多的,它基于 @Controller 注解类上的 @RequestMapping 注解,从类型和方法级别创建请求映射信息 RequestMappingInfo。

HandlerMapping

HandlerMapping 是一个接口,里面主要有一个获取处理器执行链的方法 HandlerExecutionChain getHandler(HttpServletRequest request),此接口由定义 请求处理器对象 之间映射的对象实现。

Handler(处理对象)将始终被包装成 HandlerExecutionChain(处理器执行链)实现,其中可以包含一些 HandlerInterceptor 实例。

HandlerMapping 主要实现

HandlerMapping 类图

HandlerMapping 的主要实现类主要有以下几个:

  • WelcomePageHandlerMapping:默认的欢迎首页(index.html)映射器。

  • SimpleUrlHandlerMapping:将 URLs 映射到请求处理器的 bean,支持映射到 bean 实例 和 bean names。

    语法:PATH=HANDLER_BEAN_NAME,示例如下。

    /welcome.html=ticketController
    /show.html=ticketController
    
  • RequestMappingHandlerMapping:基于 @Controller 注解类上的 @RequestMapping 注解,从类型和方法级别创建请求映射信息 RequestMappingInfo。开发中用到最多的。

  • BeanNameUrlHandlerMapping:从 URLs 映射到名称以斜杠("/")开头的 bean,这是 DispatcherServlet 和 RequestMappingHandlerMapping 一起使用的默认实现。

    例如,一个进入的请求 URL /foo 映射到一个名为 /foo 的处理器,或多个如 /foo, /foo2 映射到单个处理器。

    支持直接匹配和模式匹配,如 /test 映射到 /test/test 映射到 /t*

HandlerMapping 源码分析

  1. DispatcherServlet 里的 doService 方法中调用 doDispatch 方法。

  2. doDispatch 方法中获取处理器执行链,实际是处理器映射器,被包装成了 HandlerExecutionChain。

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
    
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
    
            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
    
                // Determine handler for the current request.
                // 决定那个处理器来处理当前请求
                mappedHandler = getHandler(processedRequest);
    
                //...............
            }
        }
    }
    
  3. 应用启动完成之后,在接收到第一个请求(HttpServletRequest)时初始化 WebApplicationContext,初始化同时调用 onRefresh 方法执行初始化 Servlet 需要用到的策略对象,其中包括初始化处理器映射器(HandlerMapping),实际是将 Spring 容器(ApplicationContext 上下文中)中实现 HandlerMapping 接口的 Bean 装入一个 List 。

    DispatcherServlet.java

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }
    
    protected void initStrategies(ApplicationContext context) {
        // 初始化文件上传解析器
        initMultipartResolver(context);
        // 初始化本地解析器
        initLocaleResolver(context);
        // 初始化主题解析器
        initThemeResolver(context);
        // 初始化处理器映射器
        initHandlerMappings(context);
        // 初始化处理器适配器
        initHandlerAdapters(context);
        // 初始化处理器异常解决器
        initHandlerExceptionResolvers(context);
        // 初始化请求到视图名的翻译器
        initRequestToViewNameTranslator(context);
        // 初始化视图解析器
        initViewResolvers(context);
        initFlashMapManager(context);
    }
    

    initHandlerMappings(context) 初始化 处理器映射器,保存到 handlerMappings

    @Nullable
    private List<HandlerMapping> handlerMappings;
    
    private void initHandlerMappings(ApplicationContext context) {
            this.handlerMappings = null;
    
            if (this.detectAllHandlerMappings) {
                // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
                // 检查 ApplicationContext 中所有 HandlerMappings
                Map<String, HandlerMapping> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
                if (!matchingBeans.isEmpty()) {
                    // 封装成一个 List
                    this.handlerMappings = new ArrayList<>(matchingBeans.values());
                    // We keep HandlerMappings in sorted order.
                    AnnotationAwareOrderComparator.sort(this.handlerMappings);
                }
            }
            else {
                try {
                    HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                    this.handlerMappings = Collections.singletonList(hm);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Ignore, we'll add a default HandlerMapping later.
                }
            }
    
            // Ensure we have at least one HandlerMapping, by registering
            // a default HandlerMapping if no other mappings are found.
            if (this.handlerMappings == null) {
                this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
                if (logger.isTraceEnabled()) {
                    logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                            "': using default strategies from DispatcherServlet.properties");
                }
            }
        }
    

    然后遍历 List<HandlerMapping>,获取与请求匹配的处理器。

    @Nullable
    private List<HandlerMapping> handlerMappings;
    
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                // 根据请求获取处理器映射器
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
    

    getHandler(request)底层是根据 request 的 URI 查找处理器。

    @Override
        @Nullable
        public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            //根据给定的 request 获取处理器
            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);
            //...........省略...........
            return executionChain;
        }
    

    RequestMappingInfoHandlerMapping.java

    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
        try {
            // 调用 AbstractHandlerMethodMapping 父类方法
            return super.getHandlerInternal(request);
        }
        finally {
            ProducesRequestCondition.clearMediaTypesAttribute(request);
        }
    }
    

    AbstractHandlerMethodMapping.java

    private final MappingRegistry mappingRegistry = new MappingRegistry();
    
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //获取请求 URI
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        request.setAttribute(LOOKUP_PATH, lookupPath);
        this.mappingRegistry.acquireReadLock();
        try {
            //根据 URI 找到其映射的处理器
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }
    
    /**
     * 查找当前请求的最佳匹配处理程序方法。 如果找到多个匹配项,则选择最佳匹配项。
     * lookupPath 
     */
    @Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        // 重点, 从初始化的 mappingRegistry 中获取匹配的路径
        List<T> directPathMatches = this.mappingRegistry .getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            // 匹配处理
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().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.getMappings().keySet(), lookupPath, request);
        }
    }
    
    /**
     * MappingRegistry 是 AbstractHandlerMethodMapping 的内部类
     * 维护与处理器方法的所有映射、公开方法以执行查找并提供并发访问的注册表
     * MappingRegistry 的初始化见下面章节
     */
    class 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();
        //........省略.........
    }
    
  4. 前提是,HandlerMapping 的实现类在注册为 Bean 后会执行最终的初始化配置,即调 afterPropertiesSet方法执行 initHandlerMethods()

    initHandlerMethods 方法遍历 ApplicationContext 中的 Bean,检测是否为处理器方法,即判断这个 bean 是否有 @Controller 或 @RequestMapping 注解,如果有则认为是处理器控制器,然后封装处理器方法与映射信息,将其注册(存储)到 MappingRegistry 中的各个 Map 或 List 中,完成 URI处理器映射的初始化。

    AbstractHandlerMethodMapping.java

    @Override
    public void afterPropertiesSet() {
        //初始化
        initHandlerMethods();
    }
    
    protected void initHandlerMethods() {
        // 遍历所有的 beanName
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                // 根据 beanName 判断处理
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }
    
    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);
        }
    }
    

    RequestMappingHandlerMapping.java

    判断是否为处理器

    @Override
    protected boolean isHandler(Class<?> beanType) {
        // 判断是否为处理器,即是否有 @Controller 或 @RequestMapping 注解
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
    

    AbstractHandlerMethodMapping.java

    /**
     * 检查处理器方法, handler 是 beanName
     */
    protected void detectHandlerMethods(Object handler) {
        // 拿到 bean 的 Class 对象, 即 Controller 类对象
        Class<?> handlerType = (handler instanceof String ?
                                obtainApplicationContext().getType((String) handler) : handler.getClass());
    
        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            // 获取方法, Key是方法对象, value是RequestMappingInfo对象,即 @RequestMapping注解的信息
            // RequestMappingInfo 包含路径,请求类型,params,head,consume,produce等信息
            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 对象,实际取的就是 method 对象
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                   // 注册处理器方法
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
    
    /**
     * 注册处理器方法及其唯一映射。在启动时为每个检测到的处理器方法调用
     */
    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        this.mappingRegistry.register(mapping, handler, method);
    }
    
  5. MappingRegistry 是 AbstractHandlerMethodMapping 的内部类,

    /**
     * MappingRegistry 是内部类
     * 注册处理器方法及其唯一映射
     * 实际就是抽取处理器方法信息和映射信息, 封装成不同的 List 和 Map, 完成初始化
     * 以备后续有请求进来后, 根据 request 直接从这些 List 或 Map 中找到映射的处理器
     */
    class 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();
    
        //........省略......
    
        public void register(T mapping, Object handler, Method method) {
            // Assert that the handler method is not a suspending one.
            if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
                throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
            }
            this.readWriteLock.writeLock().lock();
            try {
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                validateMethodMapping(handlerMethod, mapping);
                //mapping 
                this.mappingLookup.put(mapping, handlerMethod);
    
                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    // url
                    this.urlLookup.add(url, mapping);
                }
    
                String name = null;
                if (getNamingStrategy() != null) {
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    // mappingName
                    addMappingName(name, handlerMethod);
                }
    
                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    // cors
                    this.corsLookup.put(handlerMethod, corsConfig);
                }
                // 映射的注册信息
                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }
        //.........省略............
    }
    

RequestMappingHandlerMapping

获取请求映射处理器

可以用于请求拦截器 或 Controller 层的 AOP ,统一打印请求映射处理信息。

@Autowired
@Qualifier(value = "requestMappingHandlerMapping")
private HandlerMapping requestMappingHandlerMapping;

// 处理器执行链
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
HandlerMethod handlerMethod = (HandlerMethod) handlerExecutionChain.getHandler();
MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
for (MethodParameter parameter : methodParameters) {
    String string = parameter.getExecutable().toGenericString();
    logger.info("Request Controller:{}", string);
}

输出信息:

Request Controller:public com.clearofchina.core.model.ResponseModel com.clearofchina.hisp.controller.FileInfoController.detail(com.clearofchina.hisp.entity.vo.IdVO)

获取所有映射的 URI

获取所有 @Controller 注解类上的 @RequestMapping 注解映射的 URI。

@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;

//@Autowired
//private WebApplicationContext webApplicationContext;

//RequestMappingHandlerMapping handlerMapping = webApplicationContext.getBean(RequestMappingHandlerMapping.class);

//获取 WebApplicationContext 来获取 requestMappingHandlerMapping
//WebApplicationContext webApplicationContext = (WebApplicationContext) request.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//RequestMappingHandlerMapping handlerMapping = webApplicationContext.getBean("requestMappingHandlerMapping")

Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
List<Map<String, String>> list = new ArrayList<>();
Set<String> uriSet = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
    Map<String, String> controllerMap = new HashMap<>();

    RequestMappingInfo mappingInfo = entry.getKey();
    HandlerMethod handlerMethod = entry.getValue();

    Set<String> patternSet = mappingInfo.getPatternsCondition().getPatterns();
    Set<RequestMethod> methodSet = mappingInfo.getMethodsCondition().getMethods();
    //            uriSet.addAll(patterns);
    for (String pattern : patternSet) {
        uriSet.add(pattern);
        controllerMap.put("url", pattern);
    }

    // 类注解
    RequestMapping annotation = handlerMethod.getMethod().getDeclaringClass().getAnnotation(RequestMapping.class);
    // 方法注解
    RequestMapping methodAnnotation = handlerMethod.getMethodAnnotation(RequestMapping.class);

    // 类名
    controllerMap.put("className", handlerMethod.getMethod().getDeclaringClass().getName());
    // 方法名
    controllerMap.put("method", handlerMethod.getMethod().getName());
    // 方法类型
    for (RequestMethod requestMethod : methodSet) {
        controllerMap.put("type", requestMethod.toString());
    }
    list.add(controllerMap);
}
logger.info("url set:" + JSON.toJSONString(uriSet));
logger.info("url controller:" + JSON.toJSONString(list));

Set<String> uriSet 打印:

[
    "/error",
    "/fileInfo/detail",
    "/downloadLog/pageList",
    "/downloadLog/detail"
]

List<Map<String, String>> list 打印:

[
    {
        "method":"detail",
        "className":"com.clearofchina.hisp.controller.DownloadLogController",
        "type":"GET",
        "url":"/downloadLog/detail"
    },
    {
        "method":"pageList",
        "className":"com.clearofchina.hisp.controller.DownloadLogController",
        "type":"GET",
        "url":"/downloadLog/pageList"
    },
    {
        "method":"detail",
        "className":"com.clearofchina.hisp.controller.FileInfoController",
        "type":"GET",
        "url":"/fileInfo/detail"
    },
    {
        "method":"error",
        "className":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController",
        "url":"/error"
    },
    {
        "method":"errorHtml",
        "className":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController",
        "url":"/error"
    }
]

获取所有Controller的beanName

@Autowired
private WebApplicationContext webApplicationContext;
String[] beanNames = webApplicationContext.getBeanNamesForAnnotation(Controller.class);

获取Spring容器中所有beanName

@Autowired
private WebApplicationContext webApplicationContext;
String[] beanNames = webApplicationContext.getBeanDefinitionNames();

// 或者
@Component
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
}
String[] beanNames = SpringContextHolder.getApplicationContext().getBeanDefinitionNames();

相关参考

  1. SpringMVC(关于HandlerMapping执行流程原理分析)
更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: