Spring中RequestMappingInfo的扫描和注册

在开发中我们只需要在控制器中编写好handler方法,并配置好映射,Spring就会把请求自动导向我们写好的方法。Spring在收到请求时肯定是需要根据请求的URL来匹配handler,这是肯定需要知道有哪些handler。本文来分析Spring是怎么知道有哪些handler的,以及是怎么注册的?


一般我们会在控制器类中定义处理器方法,比如:

java
@Controller
public class UserController {
	@RequestMapping(value="/login")
	@ResponseBody
	public LoginResult login(User user) {
		// ...
	} 
}

控制器类被@Controller注解修饰,处理器方法被@RequestMapping修饰。处理器方法的扫描工作是在RequestMappingHandlerMapping类中的afterPropertiesSet方法触发的,这是Spring的bean生命周期中的方法,所以先来介绍一下RequestMappingHandlerMappingbean的创建。

RequestMappingHandlerMappingbean的创建

Spring Boot提供了一个WebMvcAutoConfigurationAdapter,在该类中存在一个叫作EnableWebMvcConfiguration的内部类,

v2.7.x
java
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java
/**
 * Configuration equivalent to {@code @EnableWebMvc}.
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    @Override
    protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
        // 默认是null
        if (this.mvcRegistrations != null) {
            RequestMappingHandlerMapping mapping = this.mvcRegistrations.getRequestMappingHandlerMapping();
            if (mapping != null) {
                return mapping;
            }
        }
        return super.createRequestMappingHandlerMapping();
    }
}

注意该类继承了DelegatingWebMvcConfiguration,而后者又继承了WebMvcConfigurationSupport类。在WebMvcConfigurationSupport中会创建并注册RequestMappingHandlerMapping类型的bean。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java
/**
 * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
 * requests to annotated controllers.
 */
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
        @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
        @Qualifier("mvcConversionService") FormattingConversionService conversionService,
        @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

    // 创建RequestMappingHandlerMapping
    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    mapping.setOrder(0);
    // 设置拦截器
    mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
    // 设置内容协商管理器
    mapping.setContentNegotiationManager(contentNegotiationManager);
    // 设置CORS配置
    mapping.setCorsConfigurations(getCorsConfigurations());

    // 获取路径匹配配置器
    PathMatchConfigurer pathConfig = getPathMatchConfigurer();
    if (pathConfig.getPatternParser() != null) {
        // 设置模式解析器
        mapping.setPatternParser(pathConfig.getPatternParser());
    }
    else {
        mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
        // 设置路径匹配器
        mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());

        // 是否使用后缀模式匹配
        Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
        if (useSuffixPatternMatch != null) {
            mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
        }
        Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
        if (useRegisteredSuffixPatternMatch != null) {
            mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
        }
    }
    // 是否使用带“/”结尾的匹配
    Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
    if (useTrailingSlashMatch != null) {
        mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
    }
    // 设置路径前缀
    if (pathConfig.getPathPrefixes() != null) {
        mapping.setPathPrefixes(pathConfig.getPathPrefixes());
    }

    return mapping;
}
/**
 * Protected method for plugging in a custom subclass of
 * {@link RequestMappingHandlerMapping}.
 * @since 4.0
 */
// 子类可以重写该方法
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
    return new RequestMappingHandlerMapping();
}

注意,EnableWebMvcConfiguration类中重写了createRequestMappingHandlerMapping方法。不过这都不重要,只要知道这里对RequestMappingHandlerMapping类型的bean进行了注册就可以了,后续Spring的bean工厂会创建该bean。

WebMvcConfigurationSupport类中,还注册了RequestMappingHandlerAdapterContentNegotiationManager等类型的bean。

扫描RequestMappingInfo

由于在分析代码时没有看清楚包名,导致下面的代码都是reactive包下的。不过,和servlet的实现都差不多,逻辑上都是一样的,代码细节上有些许差别。

RequestMappingHandlerMapping重写了afterPropertiesSet方法,在该方法中会触发RequestMapping处理器的扫描。

java
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java
@Override
public void afterPropertiesSet() {
    this.config = new RequestMappingInfo.BuilderConfiguration();
    this.config.setPatternParser(getPathPatternParser());
    this.config.setContentTypeResolver(getContentTypeResolver());

    // 调用父类的方法,会触发RequestMapping处理器的扫描
    super.afterPropertiesSet();
}

其实真正的扫描工作是在父类AbstractHandlerMethodMapping中完成的。

java
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java
@Override
public void afterPropertiesSet() {
    // 从所有的bean中筛选handler,并从中获取handlerMethod
    initHandlerMethods();

    // Total includes detected mappings + explicit registrations via registerMapping
    // 获取处理器方法的数量并打印日志
    int total = getHandlerMethods().size();
    if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
        logger.debug(total + " mappings in " + formatMappingName());
    }
}

核心实现在initHandlerMethods方法中。

java
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java
protected void initHandlerMethods() {
    // 获取bean工厂中的所有bean的名称
    String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class);

    // 遍历所有的bean
    for (String beanName : beanNames) {
        // 筛选掉名称以“scopedTarget.”开头的
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            Class<?> beanType = null;
            try {
                // 获取bean的类型
                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);
                }
            }
            // 判断该bean的类型是不是handler
            if (beanType != null && isHandler(beanType)) {
                // 扫描处理器方法
                detectHandlerMethods(beanName);
            }
        }
    }
    // 默认是空操作
    handlerMethodsInitialized(getHandlerMethods());
}

其中遍历所有的bean,并调用isHandler方法判断其类型属不属于是handler,这是一个模板方法,由子类来实现。

java
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java
@Override
protected boolean isHandler(Class<?> beanType) {
    // 只要类被这两个注解修饰就被认为是Handler
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

可以看到,这里判断了类上是否被@Controller或者@RequestMapping注解修饰,如果是,则认为是handler。如果是handler, 则会调用detectHandlerMethods方法来扫描handler方法。

java
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java
protected void detectHandlerMethods(final Object handler) {
    // 获取处理器类型
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 获取类中的方法
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                // getMappingForMethod方法用于获取处理器方法的映射,这是一个模板方法,由子类实现
                (MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType));
        if (logger.isTraceEnabled()) {
            logger.trace(formatMappings(userType, methods));
        }
        else if (mappingsLogger.isDebugEnabled()) {
            mappingsLogger.debug(formatMappings(userType, methods));
        }
        // 遍历方法
        methods.forEach((method, mapping) -> {
            // 对方法进行封装一下
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            // 注册处理器方法
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

该方法中主要完成了两件事情:

  • 获取处理器方法;
  • 注册处理器方法;

获取处理器方法

这个步骤借助MethodIntrospector工具类来完成。

selectMethods
doWithMethods
<
>
java
spring-core/src/main/java/org/springframework/core/MethodIntrospector.java
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
    final Map<Method, T> methodMap = new LinkedHashMap<>();
    Set<Class<?>> handlerTypes = new LinkedHashSet<>();
    Class<?> specificHandlerType = null;

    if (!Proxy.isProxyClass(targetType)) { // 如果不是JDK代理类
        // 获取类本身或CGLIB代理类的父类
        specificHandlerType = ClassUtils.getUserClass(targetType);
        handlerTypes.add(specificHandlerType);
    }
    /*
     * 获取所有实现的接口。
     * 为什么这里没有添加targetType本身?
     * 注意上面if代码块中,获取用户类就可以获取targetType。
     */
    handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

    // 遍历所有类型
    for (Class<?> currentHandlerType : handlerTypes) {
        final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

        ReflectionUtils.doWithMethods(currentHandlerType, method -> {
            Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
            // 检查方法是否是handler方法
            T result = metadataLookup.inspect(specificMethod);
            if (result != null) {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                // 如果存在桥接方法,这里再次检查一下桥接方法是否是handler
                if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                    // 添加方法和请求映射信息的映射关系
                    methodMap.put(specificMethod, result);
                }
            }
        }, ReflectionUtils.USER_DECLARED_METHODS);
    }

    return methodMap;
}
java
spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
// 用于筛选掉桥接,合成方法,以及Object类中的方法
public static final MethodFilter USER_DECLARED_METHODS =
        (method -> !method.isBridge() && !method.isSynthetic() && (method.getDeclaringClass() != Object.class));
public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
    if (mf == USER_DECLARED_METHODS && clazz == Object.class) {
        // nothing to introspect
        return;
    }
    // 获取类中声明的所有方法
    Method[] methods = getDeclaredMethods(clazz, false);
    // 遍历方法列表
    for (Method method : methods) {
        // 过滤掉不匹配的方法
        if (mf != null && !mf.matches(method)) {
            continue;
        }
        try {
            // 处理该方法
            mc.doWith(method);
        }
        catch (IllegalAccessException ex) {
            throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
        }
    }
    // 向上递归父类或父接口
    // Keep backing up the inheritance hierarchy.
    if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {
        doWithMethods(clazz.getSuperclass(), mc, mf);
    }
    else if (clazz.isInterface()) {
        for (Class<?> superIfc : clazz.getInterfaces()) {
            doWithMethods(superIfc, mc, mf);
        }
    }
}

这个方法中主要对当前类及其父类和所有父接口中的方法进行扫描、筛选处理。扫描和基本筛选是在doWithMethods方法中进行的,而判断方法是不是handler方法则是通过MetadataLookup来实现的,如果返回的不是null则认为是handler方法。 MetadataLookup是一个函数式接口,AbstractHandlerMethodMapping传递过来的lambda函数中调用的是getMappingForMethod方法,这是一个模板方法,所以接下来参考一下RequestMappingHandlerMapping中的实现。

java
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 为方法创建请求映射信息
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // 为类创建请求映射信息
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // 如果类上也配置了请求映射,则进行合并
            info = typeInfo.combine(info);
        }
        for (Map.Entry<String, Predicate<Class<?>>> entry : this.pathPrefixes.entrySet()) {
            if (entry.getValue().test(handlerType)) {
                String prefix = entry.getKey();
                if (this.embeddedValueResolver != null) {
                    prefix = this.embeddedValueResolver.resolveStringValue(prefix);
                }
                info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
                break;
            }
        }
    }
    return info;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    // 获取方法上的@RequesMapping注解
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    RequestCondition<?> condition = (element instanceof Class ?
            getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    // 根据注解信息,创建RequestMappingInfo对象
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
protected RequestMappingInfo createRequestMappingInfo(
        RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

    // 将@RequestMapping注解中的属性封装为RequestMappingInfo
    RequestMappingInfo.Builder builder = RequestMappingInfo
            .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
            .methods(requestMapping.method())
            .params(requestMapping.params())
            .headers(requestMapping.headers())
            .consumes(requestMapping.consumes())
            .produces(requestMapping.produces())
            .mappingName(requestMapping.name());
    if (customCondition != null) {
        builder.customCondition(customCondition);
    }
    return builder.options(this.config).build();
}

其实可以发现,这里就是在获取方法上的@RequestMapping注解信息,如果获取到了,就认为是handler方法。

注册处理器方法

java
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java
private final MappingRegistry mappingRegistry = new MappingRegistry();
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    this.mappingRegistry.register(mapping, handler, method);
}

MappingRegistryAbstractHandlerMethodMapping中的内部类,负责mapping的注册。

java
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();

private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        // 创建处理器方法对象
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        // 验证方法映射
        validateMethodMapping(handlerMethod, mapping);

        // 获取路径
        Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
        for (String path : directPaths) {
            // 添加路径到mapping的映射
            this.pathLookup.add(path, mapping);
        }

        // 初始化CORS配置
        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            corsConfig.validateAllowCredentials();
            this.corsLookup.put(handlerMethod, corsConfig);
        }

        // 添加mapping到handler的映射
        this.registry.put(mapping,
                new MappingRegistration<>(mapping, handlerMethod, directPaths, corsConfig != null));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

总结

本文先分析了RequestMappingHandlerMapping这个bean是怎么创建的,然后分析了是怎么扫描并注册RequestMappingInfo的。Spring在处理请求时,需要根据请求路径查已注册的handler,这个过程还会涉及到本文的分析的内容的。