RequestMappingHandlerMapping的原理

1.RequestMappingHandlerMapping的初始化流程

首先看下RequestMappingHandlerMapping的类图


image.png

RequestMappingHandlerMapping 实现了InitializingBean
在AbstractHandlerMethodMapping 中afterPropertiesSet(),会在容器注入的进去的时候执行。

@Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }

    /**
     * Scan beans in the ApplicationContext, detect and register handler methods.
     * @see #isHandler(Class)
     * @see #getMappingForMethod(Method, Class)
     * @see #handlerMethodsInitialized(Map)
     */
    protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));

        for (String beanName : beanNames) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class beanType = null;
                try {
                    beanType = getApplicationContext().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);
                    }
                }
                if (beanType != null && isHandler(beanType)) {
                    detectHandlerMethods(beanName);
                }
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void detectHandlerMethods(final Object handler) {
        Class handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());
        final Class userType = ClassUtils.getUserClass(handlerType);

        Map methods = MethodIntrospector.selectMethods(userType,
                new MethodIntrospector.MetadataLookup() {
                    @Override
                    public T inspect(Method method) {
                        try {
                            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);
        }
        for (Map.Entry 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) {
         // 注入扫描到handler的方法
        this.mappingRegistry.register(mapping, handler, method);
    }

由上图可知,RequestMappingHandlerMapping大致工作流程如下:

  • RequestMappingHandlerMapping 向容器中注册的时候,检测到实现了 InitializingBean接口,容器去执行afterPropertiesSet(),在afterPropertiesSet中完成Controller中完成方法的映射
  • initHandlerMethods 首先获取容器中全部的beanNames。
  • 根据名字获取类,判断该类是不是要转换的类型
protected boolean isHandler(Class beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
  • 调用detectHandlerMethods开始做类型和路径映射转换
   Map methods = MethodIntrospector.selectMethods(userType,
               new MethodIntrospector.MetadataLookup() {
                   @Override
                   public T inspect(Method method) {
                       try {
                           return getMappingForMethod(method, userType);
                       }
                       catch (Throwable ex) {
                           throw new IllegalStateException("Invalid mapping on handler class [" +
                                   userType.getName() + "]: " + method, ex);
                       }
                   }
               });

根据类型的方法做映射转换

  • 此处应该是getMappingForMethod 是扩展点,可以继承RequestMappingHandlerMapping重写getMappingForMethod实现自己的映射规则。
    例如:
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomerRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) {
        RequestMappingInfo requestMappingInfo = super.getMappingForMethod(method, handlerType);
        if (requestMappingInfo != null) {
            return requestMappingInfo;
        }
        return createCustomRequestMappingInfo(method);
    }


    private RequestMappingInfo createCustomRequestMappingInfo(Method method) {
        RestMapping mapping = AnnotatedElementUtils.findMergedAnnotation(method, RestMapping.class);
        if (mapping != null) {
            return RequestMappingInfo.paths(Samples.URI_PREFIX_OF_API + mapping.value())
                    .methods(mapping.method())
                    .build();
        }
        return null;
    }
}

如何通过RequestMappingHandlerMapping 找到请求对应的handler(方法)

spring mvc中,当请求到来的时候,会被统一到DispatcherServlet的doDispatch去执行。
看下doDispatch的部分代码,

              processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

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

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

mappedHandler = getHandler(processedRequest); 就是根据请求获取处理的handler。

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;
    }

此方法会根据注入到DispatcherServlet的RequestMappingHandlerMapping 集合来找到该请求对应的handler用于处理该请求。

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;
    }

RequestMappingHandlerMapping 如何注入到DispatcherServlet中

在DispatcherServlet初始化的时候,会调用内部的的一个onRefresh方法。最终会调用initHandlerMappings方法。

    /**
     * This implementation calls {@link #initStrategies}.
     */
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * 

May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { 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.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }

initHandlerMappings方法获取注入到容器中的全部的RequestMappingHandlerMapping,按照规则排序。赋值给本身的handlerMappings。当请求到来的时候会遍历handlerMappings,找到合适初始方法进行处理。如下是DispatcherServlet当请求达到的时候遍历handlerMappings。

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;
    }

RequestMappingHandlerMapping 的注入容器

RequestMappingHandlerMapping 作为spring mvc 路由组件,起到了从路径到指定方法的转换作用。只有注入到容器中,在请求达到的时候,从容器中获取到RequestMappingHandlerMapping 在通过RequestMappingHandlerMapping获取到handler用于处理请求。
RequestMappingHandlerMapping的注入分为四种:

  1. 如果是基于xml配置的springmvc项目 在springmvc的配置文件中加入 ,在容器解析配置的文件的时候,会初始化RequestMappingHandlerMapping 并注入到容器中。
  2. 如果是基于注解的形式springmvc项目。可以使用@EnableWebMvc来初始化RequestMappingHandlerMapping并注入到容器中。具体实现请看
    Spring注解开发
  3. 当基于spring boot 开发的时候 WebMvcAutoConfiguration 会自动加载web开发相关的配置。其中EnableWebMvcConfiguration 继承自DelegatingWebMvcConfiguration,DelegatingWebMvcConfiguration的内部原理 在Spring注解开发 已经做了阐述。EnableWebMvcConfiguration在WebMvcAutoConfiguration中一@Bean的形式被注入到容器中。因此RequestMappingHandlerMapping 此时会被创建注入到容器中。
  4. 自定义RequestMappingHandlerMapping,注入到容器中。可以采用xml的形式注入,也可以通过@Bean的形式注入;例如
 @Bean
    public RequestMappingHandlerMapping customRequestMappingHandlerMapping() {
        CustomerRequestMappingHandlerMapping mappingHandlerMapping = new CustomerRequestMappingHandlerMapping();
        mappingHandlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE);//设置排序
        return mappingHandlerMapping;
    }

你可能感兴趣的:(RequestMappingHandlerMapping的原理)