springmvc--14--@RequestMapping注解解析原理

springmvc–@RequestMapping注解解析原理

文章目录

1 前言

之前我在springmvc–HandlerMapping处理器映射器这篇文章中解释过如何从RequestMappingHandlerMapping处理器映射器中得到处理对应请求的HandlerExecutionChain处理器执行器链。当时并没有解释springmvc是什么时候解析@RequestMapping注解的,所以本文就补充一下这部分内容。

2 大概原理

先来看一下RequestMappingHandlerMapping的类图

在这里插入图片描述

InitializingBean接口是解析@RequestMapping注解的核心,它会在RequestMappingHandlerMapping初始化阶段扫描所有的@Controller注解的类,并解析类中标有@RequestMapping注解的方法。

3 具体原理

那么接下来的篇幅呢,我们就重点看一下InitializingBean接口的afterPropertiesSet()方法的具体实现。

3.1 RequestMappingHandlerMapping类的afterPropertiesSet()方法

直接定位到RequestMappingHandlerMapping类的afterPropertiesSet()方法

private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();


@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
    //创建请求映射的配置容器对象,见5
    this.config = new RequestMappingInfo.BuilderConfiguration();
    //将处理器映射器持有的UrlPathHelper对象赋值给配置容器,见5.1
    this.config.setUrlPathHelper(getUrlPathHelper());
    //将处理器映射器持有的PathMatcher对象赋值给配置容器,见5.2
    this.config.setPathMatcher(getPathMatcher());
    //是否使用注册的后缀路径进行匹配,默认为true,已过时,直接忽略
    this.config.setSuffixPatternMatch(useSuffixPatternMatch());
    //是否与URL匹配,无论是否存在斜杠,默认为true,见5.3
    this.config.setTrailingSlashMatch(useTrailingSlashMatch());
    //是否使用注册的后缀路径进行匹配,默认为true,已过时,直接忽略
    this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
    //将处理器映射器持有的内容协商管理器对象赋值给配置容器,见5.4
    this.config.setContentNegotiationManager(getContentNegotiationManager());

    //回调了父类的afterPropertiesSet()方法,见3.2
    super.afterPropertiesSet();
}

重写的afterPropertiesSet()方法只是创建一个BuilderConfiguration配置对象,用来保存所有请求映射信息对象的公有配置。

3.2 AbstractHandlerMethodMapping类的afterPropertiesSet()方法

它也实现了InitializingBean接口的afterPropertiesSet()方法,实际上RequestMappingHandlerMapping就是再次重写该类的afterPropertiesSet()方法

/**
 * Detects handler methods at initialization.
 * @see #initHandlerMethods
 */
@Override
public void afterPropertiesSet() {
    initHandlerMethods();
}

方法内部定义了一个initHandlerMethods()方法,这个方法用来初始化处理器方法。

接下来我们就看看AbstractHandlerMethodMapping类的initHandlerMethods()方法干了啥事?

private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #getCandidateBeanNames()
 * @see #processCandidateBean
 * @see #handlerMethodsInitialized
 */
protected void initHandlerMethods() {
    //获取容器中所有的beanName,并遍历,见3.2.1
    for (String beanName : getCandidateBeanNames()) {
        /**
         * "scopedTarget."开头的beanName实际上是原始bean
         * 真实的beanName对应于spring创建的作用域代理BeanDefinition
         * @Scope注解指定作用域代理
         */
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            /**
             * 处理候选bean,见3.2.2
             * 解析bean中的@RequestMapping注解,并生成对应RequestMappingInfo
             */
            processCandidateBean(beanName);
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

3.2.1 获取spring容器中所有的beanName

//是否获取父容器中的beanName,默认不获取
private boolean detectHandlerMethodsInAncestorContexts = false;


/**
 * Determine the names of candidate beans in the application context.
 * @since 5.1
 * @see #setDetectHandlerMethodsInAncestorContexts
 * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors
 */
protected String[] getCandidateBeanNames() {
    return (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
            obtainApplicationContext().getBeanNamesForType(Object.class));
}

这里默认获取当前容器中所有的beanName,如果想获取父容器的,可以修改detectHandlerMethodsInAncestorContexts字段值为true,它就会调用BeanFactoryUtils.beanNamesForTypeIncludingAncestors()方法去父容器中匹配对应类型beanbeanName

3.2.2 处理候选bean

/**
 * Determine the type of the specified candidate bean and call
 * {@link #detectHandlerMethods} if identified as a handler type.
 * <p>This implementation avoids bean creation through checking
 * {@link org.springframework.beans.factory.BeanFactory#getType}
 * and calling {@link #detectHandlerMethods} with the bean name.
 * @param beanName the name of the candidate bean
 * @since 5.1
 * @see #isHandler
 * @see #detectHandlerMethods
 */
protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    try {
        //获取beanName对应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);
        }
    }
    /**
     * isHandler(beanType)方法,判断beanType对应的bean是不是一个处理器类
     * ,即有@Controller注解,见3.2.2.1
     */
    if (beanType != null && isHandler(beanType)) {
        //解析处理器类中的处理器方法(@RequestMapping注解),见3.2.2.2
        detectHandlerMethods(beanName);
    }
}
3.2.2.1 判断beanType对应的bean是不是一个处理器类
/**
 * Whether the given type is a handler with handler methods.
 * @param beanType the type of the bean being checked
 * @return "true" if this a handler type, "false" otherwise.
 */
protected abstract boolean isHandler(Class<?> beanType);

这是AbstractHandlerMethodMapping类的抽象方法, 具体的判断逻辑由子类RequestMappingHandlerMapping实现

/**
 * {@inheritDoc}
 * <p>Expects a handler to have either a type-level @{@link Controller}
 * annotation or a type-level @{@link RequestMapping} annotation.
 */
@Override
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

只要类上标注了@Controller注解或@RequestMapping注解,就认为该类就是一个处理器类

3.2.2.2 解析处理器类中的@RequestMapping注解
/**
 * Look for handler methods in the specified handler bean.
 * @param handler either a bean name or an actual handler instance
 * @see #getMappingForMethod
 */
protected void detectHandlerMethods(Object handler) {
    //得到处理器类的clazz对象
    Class<?> handlerType = (handler instanceof String ?
                            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        //获取用户类,也就是使用了代理类的目标类
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        /**
         * 反射遍历userType类及其父接口的所有的方法,得到符合条件的方法信息,见3.3
         * 此处会收集到所有标注了@RequestMapping注解的方法->对应的请求映射信息对象
         */
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                                                                  (MethodIntrospector.MetadataLookup<T>) method -> {
                     try {
                         /**
                          * 过滤条件,见3.4
                          * 解析方法上@RequestMapping注解,并生成对应的请求映射信息对象
                          */
                         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) -> {
            /**
             * 对方法进行验证
             * 如果方法是私有的、且非静态的、且方法所在类实现了SpringProxy接口的
             * 就抛出异常,表明该方法不符合条件
             */
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            //将处理器方法和RequestMappingInfo之间的唯一映射注册到MappingRegistry对象中
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

下面是处理器方法和RequestMappingInfo之间唯一映射的注册过程,其实就是注册到MappingRegistry对象中

//一个注册表,用来保存所有处理器方法和RequestMappingInfo之间的唯一映射关系
private final MappingRegistry mappingRegistry = new MappingRegistry();


/**
 * Register a handler method and its unique mapping. Invoked at startup for
 * each detected handler method.
 * @param handler the bean name of the handler or the handler instance
 * @param method the method to register
 * @param mapping the mapping conditions associated with the handler method
 * @throws IllegalStateException if another method was already registered
 * under the same mapping
 */
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    //将映射关系保存到注册表中,见7.1
    this.mappingRegistry.register(mapping, handler, method);
}

可以发现,这个方法就是最核心的方法, 它会遍历容器中所有的BeanDefinition,反射解析这些BeanDefinition对应类方法上的@RequestMapping注解,并生成RequestMappingInfo对象。最后将标注@RequestMapping注解的方法和RequestMappingInfo之间的唯一映射注册到MappingRegistry对象中。

3.3 selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup)方法,反射遍历某个类及其父接口的所有的方法,得到符合条件的方法信息集合

/**
 * Select methods on the given target type based on the lookup of associated metadata.
 * <p>Callers define methods of interest through the {@link MetadataLookup} parameter,
 * allowing to collect the associated metadata into the result map.
 * @param targetType the target type to search methods on
 * @param metadataLookup a {@link MetadataLookup} callback to inspect methods of interest,
 * returning non-null metadata to be associated with a given method if there is a match,
 * or {@code null} for no match
 * @return the selected methods associated with their metadata (in the order of retrieval),
 * or an empty map in case of no match
 */
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)) {
        specificHandlerType = ClassUtils.getUserClass(targetType);
        handlerTypes.add(specificHandlerType);
    }
    //得到该类及其所有的父接口
    handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

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

        /**
         * 使用反射工具类遍历指定类的所有方法
         * ReflectionUtils.USER_DECLARED_METHODS是一个方法过滤器,
         * 过滤得到所有非桥接且非Synthetic的方法
         */
        ReflectionUtils.doWithMethods(currentHandlerType, method -> {
            Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
            /**
             * 调用MetadataLookup接口的inspect()方法
             * 实际上就是调用getMappingForMethod(method, userType)方法,
             * java8的函数式编程
             */
            T result = metadataLookup.inspect(specificMethod);
            if (result != null) {
                //获取当前方法的桥接方法
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                //桥接方法和原来的方法是同一个,那么就将该方法保存下来
                if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                    methodMap.put(specificMethod, result);
                }
            }
        }, ReflectionUtils.USER_DECLARED_METHODS);
    }

    return methodMap;
}

该方法是MethodIntrospector方法内省工具类的一个静态方法,用户通过指定一个MetadataLookup函数式接口过滤得到指定条件的方法对象。

3.4 getMappingForMethod(method, userType)方法,创建标注@RequestMapping注解方法的请求映射信息

该方法最早定义在AbstractHandlerMethodMapping类中,是一个抽象方法

/**
 * Provide the mapping for a handler method. A method for which no
 * mapping can be provided is not a handler method.
 * @param method the method to provide a mapping for
 * @param handlerType the handler type, possibly a sub-type of the method's
 * declaring class
 * @return the mapping, or {@code null} if the method is not mapped
 */
@Nullable
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);

具体的逻辑由子类RequestMappingHandlerMapping实现

/**
 * Uses method and type-level @{@link RequestMapping} annotations to create
 * the RequestMappingInfo.
 * @return the created RequestMappingInfo, or {@code null} if the method
 * does not have a {@code @RequestMapping} annotation.
 * @see #getCustomMethodCondition(Method)
 * @see #getCustomTypeCondition(Class)
 */
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    //为该方法创建请求映射信息。见3.4.1
    RequestMappingInfo info = createRequestMappingInfo(method);
    //方法上有@RequestMapping注解,才会去解析类上的@RequestMapping注解
    if (info != null) {
        //为该类创建请求映射信息。见3.4.2
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            //合并方法级别和类级别@RequestMapping注解对应的RequestMappingInfo,见4.3
            info = typeInfo.combine(info);
        }
        //获取路径前缀,见3.4.4
        String prefix = getPathPrefix(handlerType);
        if (prefix != null) {
            //将路径前缀拼接到@RequestMapping注解指定路径的前面
            info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
        }
    }
    return info;
}

总结一下方法流程:

  • 首先创建方法级别的@RequestMapping注解对应的RequestMappingInfo请求映射信息
  • 随后创建类级别的@RequestMapping注解对应的RequestMappingInfo请求映射信息,并将这两个对象进行合并
  • 最后检查用户是否设置了路径前缀,设置路径前缀,则根据这个路径前缀再次构建一个RequestMappingInfo请求映射信息,并与上面的RequestMappingInfo对象进行合并得到一个新的RequestMappingInfo请求映射信息

3.4.1 解析方法级别的@RequestMapping注解

/**
 * Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},
 * supplying the appropriate custom {@link RequestCondition} depending on whether
 * the supplied {@code annotatedElement} is a class or method.
 * @see #getCustomTypeCondition(Class)
 * @see #getCustomMethodCondition(Method)
 */
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    //获取方法上标注的@RequestMapping注解信息
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    /**
     * 此处element是一个Method,执行getCustomMethodCondition()方法,
     * 方法直接返回null,没有实现
     */
    RequestCondition<?> condition = (element instanceof Class ?
                                     getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    //方法上标注了注解@RequestMapping注解,就解析注解创建请求映射信息,见3.4.3
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

该方法主要做一件事,获取方法上标注的@RequestMapping注解信息,然后把创建请求映射信息的过程委派给createRequestMappingInfo(requestMapping, condition)方法完成。

本章节和3.4.2章节中的方法是同一个方法。

3.4.2 解析类级别的@RequestMapping注解

/**
 * Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},
 * supplying the appropriate custom {@link RequestCondition} depending on whether
 * the supplied {@code annotatedElement} is a class or method.
 * @see #getCustomTypeCondition(Class)
 * @see #getCustomMethodCondition(Method)
 */
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    //获取类上标注的@RequestMapping注解信息
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    /**
     * 此处element是一个Class,执行getCustomTypeCondition()方法,
     * 方法直接返回null,没有实现
     */
    RequestCondition<?> condition = (element instanceof Class ?
                                     getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    //类上标注了注解@RequestMapping注解,就解析注解创建请求映射信息,见3.4.3
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

该方法主要做一件事,获取类上标注的@RequestMapping注解信息,然后把创建请求映射信息的过程委派给createRequestMappingInfo(requestMapping, condition)方法完成。

本章节和3.4.1章节中的方法是同一个方法。

3.4.3 根据@RequestMapping注解创建请求映射信息

/**
 * Create a {@link RequestMappingInfo} from the supplied
 * {@link RequestMapping @RequestMapping} annotation, which is either
 * a directly declared annotation, a meta-annotation, or the synthesized
 * result of merging annotation attributes within an annotation hierarchy.
 */
protected RequestMappingInfo createRequestMappingInfo(
    RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

    //建造者模式,根据@RequestMapping注解属性值构建一个建造器,见6
    RequestMappingInfo.Builder builder = RequestMappingInfo
        //resolveEmbeddedValuesInPatterns()方法,解析占位符,见3.4.3.1
        .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);
    }
    //构建一个RequestMappingInfo对象,6.2
    return builder.options(this.config).build();
}

这里使用建造者模式将RequestMappingInfo对象复杂的创建过程封装起来。

3.4.3.1 解析占位符
/**
 * Resolve placeholder values in the given array of patterns.
 * @return a new array with updated patterns
 */
protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {
    if (this.embeddedValueResolver == null) {
        return patterns;
    }
    else {
        String[] resolvedPatterns = new String[patterns.length];
        for (int i = 0; i < patterns.length; i++) {
            resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
        }
        return resolvedPatterns;
    }
}

映射路径作为一个整体去解析,也就是说占位符不能是映射路径的一部分。

  • /test/${xxx}:这种不符合要求,不能解析占位符
  • ${xxx}:这种符合要求,可以解析占位符

3.4.4 获取路径前缀

//这个集合默认是没有数据的,用户可使用对应的setter方法向里面添加路径前缀
private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();


@Nullable
String getPathPrefix(Class<?> handlerType) {
    //遍历
    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);
            }
            return prefix;
        }
    }
    return null;
}

用户可以配置多个路径前缀->断言的映射关系,那么在解析@RequestMapping注解的时候,就会先断言这个类是否符合条件,符合条件,则解析路径前缀的占位符,然后拼接到@RequestMapping注解指定路径的前面。

4 RequestMappingInfo

该类封装了用户定义的请求映射信息,实际上就是用来保存一个@RequestMapping注解的解析结果

先来看一下它的类图

在这里插入图片描述

它实现了RequestCondition接口,那么这个接口是干嘛的呢?该接口提供了三个方法,分别如下所示:

public interface RequestCondition<T> {

    /**
     * 这个方法用来合并两个RequestMappingInfo,
     * 我们在解析@RequestMapping注解的时候,会分别解析方法上的@RequestMapping注解和
     * 类上的@RequestMapping注解,生成两个RequestMappingInfo对象,此时就需要调用这个方法
     * 将两个RequestMappingInfo对象的信息合并起来
     */
    T combine(T other);

    /**
     * 检查RequestMappingInfo对象对应的处理器方法是否能够处理本次请求
     * 返回null,表示不能处理
     */
    @Nullable
    T getMatchingCondition(HttpServletRequest request);

    /**
     * Compare this condition to another condition in the context of
     * a specific request. This method assumes both instances have
     * been obtained via {@link #getMatchingCondition(HttpServletRequest)}
     * to ensure they have content relevant to current request only.
     */
    int compareTo(T other, HttpServletRequest request);

}

这个接口提供了一个泛型约束,而RequestMappingInfo类的泛型约束就是它自己,如下所示:public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo>{...}

接下来我们看一下几个比较常用的方法

4.1 paths(String... paths)方法,创建一个指定路径的Builder对象

/**
 * Create a new {@code RequestMappingInfo.Builder} with the given paths.
 * @param paths the paths to use
 * @since 4.2
 */
public static Builder paths(String... paths) {
    return new DefaultBuilder(paths);
}

Builder的类型为DefaultBuilder,见6.1

4.2 getMatchingCondition(HttpServletRequest request)方法,检查当前RequestMappingInfo的所有条件是否匹配本次请求

/**
 * Checks if all conditions in this request mapping info match the provided request and returns
 * a potentially new request mapping info with conditions tailored to the current request.
 * <p>For example the returned instance may contain the subset of URL patterns that match to
 * the current request, sorted with best matching patterns on top.
 * @return a new instance in case all conditions match; or {@code null} otherwise
 */
@Override
@Nullable
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
    RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
    if (methods == null) {
        return null;
    }
    ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
    if (params == null) {
        return null;
    }
    HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
    if (headers == null) {
        return null;
    }
    ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
    if (consumes == null) {
        return null;
    }
    ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
    if (produces == null) {
        return null;
    }
    PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
    if (patterns == null) {
        return null;
    }
    RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
    if (custom == null) {
        return null;
    }

    return new RequestMappingInfo(this.name, patterns,
                                  methods, params, headers, consumes, produces, custom.getCondition());
}

其实这些条件类也都是RequestCondition接口的实现类,该方法会一个一个的去调用RequestMappingInfo类持有的条件的getMatchingCondition(request)方法, 判断是否匹配本次请求。只要有一个条件不匹配,就返回null,否则使用这些条件的对应值重新构建一个RequestMappingInfo对象。

4.3 combine(RequestMappingInfo other)方法, 合并两个RequestMappingInfo对象

/**
 * Combine "this" request mapping info (i.e. the current instance) with another request mapping info instance.
 * <p>Example: combine type- and method-level request mappings.
 * @return a new request mapping info instance; never {@code null}
 */
@Override
public RequestMappingInfo combine(RequestMappingInfo other) {
    String name = combineNames(other);
    PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
    RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
    ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
    HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
    ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
    ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
    RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);

    return new RequestMappingInfo(name, patterns,
                                  methods, params, headers, consumes, produces, custom.getCondition());
}

该方法会一个一个的去调用RequestMappingInfo类持有的条件的combine(other)方法, 合并每一个条件。最后使用合并后条件的对应值重新构建一个RequestMappingInfo对象。

5 BuilderConfiguration

该类是RequestMappingInfo类的一个嵌套类(静态内部类),它只有默认的无参构造方法。

它代表所有请求映射的建造器配置,当扫描到@RequestMapping注解方法后,解析生成RequestMappingInfo请求映射信息的时候,需要将这个配置信息覆盖到生成RequestMappingInfo对象中。

下面是一些被用到的方法的源码

5.1 setUrlPathHelper(@Nullable UrlPathHelper urlPathHelper)方法,配置UrlPathHelper

@Nullable
private UrlPathHelper urlPathHelper;

/**
 * Set a custom UrlPathHelper to use for the PatternsRequestCondition.
 * <p>By default this is not set.
 * @since 4.2.8
 */
public void setUrlPathHelper(@Nullable UrlPathHelper urlPathHelper) {
    this.urlPathHelper = urlPathHelper;
}

urlPathHelper字段对应的setter方法

5.2 setPathMatcher(@Nullable PathMatcher pathMatcher)方法, 配置PathMatcher

@Nullable
private PathMatcher pathMatcher;


/**
 * Set a custom PathMatcher to use for the PatternsRequestCondition.
 * <p>By default this is not set.
 */
public void setPathMatcher(@Nullable PathMatcher pathMatcher) {
    this.pathMatcher = pathMatcher;
}

pathMatcher字段对应的setter方法

5.3 setTrailingSlashMatch(boolean trailingSlashMatch)方法, 设置是否与URL匹配,无论是否存在斜杠

private boolean trailingSlashMatch = true;

/**
 * Set whether to apply trailing slash matching in PatternsRequestCondition.
 * <p>By default this is set to 'true'.
 */
public void setTrailingSlashMatch(boolean trailingSlashMatch) {
    this.trailingSlashMatch = trailingSlashMatch;
}

trailingSlashMatch字段对应的setter方法,默认开启总是与URL匹配

5.4 setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager)方法, 配置内容协商管理器

@Nullable
private ContentNegotiationManager contentNegotiationManager;


/**
 * Set the ContentNegotiationManager to use for the ProducesRequestCondition.
 * <p>By default this is not set.
 */
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
    this.contentNegotiationManager = contentNegotiationManager;
}

contentNegotiationManager字段对应的setter方法,默认使用处理器映射器的内容协商管理器

6 DefaultBuilder

该类是RequestMappingInfo类的一个嵌套类(静态内部类),它只提供一个有参构造方法。它代表了一个请求映射信息的建造器。

6.1 建造方法的源码

private static class DefaultBuilder implements Builder {

    //映射路径
    private String[] paths;

    //请求方式
    private RequestMethod[] methods = new RequestMethod[0];

    //请求参数
    private String[] params = new String[0];

    //请求头
    private String[] headers = new String[0];

    private String[] consumes = new String[0];

    private String[] produces = new String[0];

    //内容类型
    private boolean hasContentType;

    //接收类型
    private boolean hasAccept;

    //映射名
    @Nullable
    private String mappingName;

    @Nullable
    private RequestCondition<?> customCondition;

    //请求映射信息的建造器配置
    private BuilderConfiguration options = new BuilderConfiguration();

    public DefaultBuilder(String... paths) {
        this.paths = paths;
    }

    @Override
    public Builder paths(String... paths) {
        this.paths = paths;
        return this;
    }

    @Override
    public DefaultBuilder methods(RequestMethod... methods) {
        this.methods = methods;
        return this;
    }

    @Override
    public DefaultBuilder params(String... params) {
        this.params = params;
        return this;
    }

    @Override
    public DefaultBuilder headers(String... headers) {
        for (String header : headers) {
            this.hasContentType = this.hasContentType ||
                header.contains("Content-Type") || header.contains("content-type");
            this.hasAccept = this.hasAccept ||
                header.contains("Accept") || header.contains("accept");
        }
        this.headers = headers;
        return this;
    }

    @Override
    public DefaultBuilder consumes(String... consumes) {
        this.consumes = consumes;
        return this;
    }

    @Override
    public DefaultBuilder produces(String... produces) {
        this.produces = produces;
        return this;
    }

    @Override
    public DefaultBuilder mappingName(String name) {
        this.mappingName = name;
        return this;
    }

    @Override
    public DefaultBuilder customCondition(RequestCondition<?> condition) {
        this.customCondition = condition;
        return this;
    }

    @Override
    public Builder options(BuilderConfiguration options) {
        this.options = options;
        return this;
    }

}

上面这些方法用来保存建造RequestMappingInfo对象的配置

6.2 build()方法,创建RequestMappingInfo请求映射信息

@Override
@SuppressWarnings("deprecation")
public RequestMappingInfo build() {

    PatternsRequestCondition patternsCondition = ObjectUtils.isEmpty(this.paths) ? null :
    new PatternsRequestCondition(
        this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
        this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
        this.options.getFileExtensions());

    ContentNegotiationManager manager = this.options.getContentNegotiationManager();

    return new RequestMappingInfo(this.mappingName, patternsCondition,
                                  ObjectUtils.isEmpty(this.methods) ?
                                  null : new RequestMethodsRequestCondition(this.methods),
                                  ObjectUtils.isEmpty(this.params) ?
                                  null : new ParamsRequestCondition(this.params),
                                  ObjectUtils.isEmpty(this.headers) ?
                                  null : new HeadersRequestCondition(this.headers),
                                  ObjectUtils.isEmpty(this.consumes) && !this.hasContentType ?
                                  null : new ConsumesRequestCondition(this.consumes, this.headers),
                                  ObjectUtils.isEmpty(this.produces) && !this.hasAccept ?
                                  null : new ProducesRequestCondition(this.produces, this.headers, manager),
                                  this.customCondition);
}

创建RequestMappingInfo对象是一个复杂的过程,它将@RequestMapping注解属性值封装为一个个的RequestCondition

RequestCondition是请求条件,HandlerMapping获取处理请求的处理器方法时,必须满足所有的RequestCondition,当前RequestMappingInfo对应的标注@RequestMapping注解的方法才会被认为可处理本次请求。

7 MappingRegistry

这是AbstractHandlerMethodMapping的一个内部类。它是一个注册表,用来维护所有标注@RequestMapping注解方法的映射关系。

7.1 register(T mapping, Object handler, Method method)方法, 注册标注@RequestMapping注解方法的所有映射

private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

//RequestMappingInfo到处理器方法之间的映射关系
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

//请求路径到RequestMappingInfo之间的映射关系
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

//处理器方法和跨域配置之间的映射关系
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

//MappingRegistration保存着标注@RequestMapping注解方法的所有映射关系,见8
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();



/**
 * @param mapping	实际上就是RequestMappingInfo
 * @param handler	处理器方法所在类的beanName
 * @param method	标注@RequestMapping注解的方法
 */
public void register(T mapping, Object handler, Method method) {
    // Assert that the handler method is not a suspending one.
    if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
            throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
        }
    }

    //加锁,注册过程涉及到多个集合,必须加锁来保证线程安全
    this.readWriteLock.writeLock().lock();
    try {
        //初见处理器方法对象,见7.4.1
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        //校验映射关系,判断注册表中是否已经存在这个映射关系,已经存在就不能向里面注册了,见7.2
        validateMethodMapping(handlerMethod, mapping);
        //注册表维护的第1种映射关系
        this.mappingLookup.put(mapping, handlerMethod);

        //获取该方法对应的请求路径
        List<String> directUrls = getDirectUrls(mapping);
        for (String url : directUrls) {
            //注册表维护的第2种映射关系
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        //获取映射关系的命名策略,见7.4.2
        if (getNamingStrategy() != null) {
            //生成名字
            name = getNamingStrategy().getName(handlerMethod, mapping);
            //将名字和处理器方法添加到注册表中
            addMappingName(name, handlerMethod);
        }

        //跨域配置,会去解析方法和类上的@CrossOrigin注解,生成对应的跨域配置,见7.4.3
        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            /**
             * 注册表维护的第3种映射关系
             * 注册跨域配置和处理器方法之间的映射关系
             */
            this.corsLookup.put(handlerMethod, corsConfig);
        }

        /**
         * 注册表维护的第4种映射关系
         * MappingRegistration保存着标注@RequestMapping注解方法的所有映射关系,见8
         */
        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
    }
    finally {
        //释放锁
        this.readWriteLock.writeLock().unlock();
    }
}

注册表的register(T mapping, Object handler, Method method)方法, 一共会注册4种映射关系

  1. RequestMappingInfo->HandlerMethod
  2. url->RequestMappingInfo
  3. HandlerMethod->CorsConfiguration
  4. RequestMappingInfo->MappingRegistration

7.2 validateMethodMapping(HandlerMethod handlerMethod, T mapping)方法, 判断是否已经存在到该标注@RequestMapping注解方法的映射

private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
    // Assert that the supplied mapping is unique.
    HandlerMethod existingHandlerMethod = this.mappingLookup.get(mapping);
    if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) {
        throw new IllegalStateException(
            "Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" +
            handlerMethod + "\nto " + mapping + ": There is already '" +
            existingHandlerMethod.getBean() + "' bean method\n" + existingHandlerMethod + " mapped.");
    }
}

校验映射的唯一性

7.3 addMappingName(String name, HandlerMethod handlerMethod)方法, 缓存映射名和处理器方法

private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();


private void addMappingName(String name, HandlerMethod handlerMethod) {
    //先从缓存中取
    List<HandlerMethod> oldList = this.nameLookup.get(name);
    if (oldList == null) {
        oldList = Collections.emptyList();
    }

    //处理器方法和缓存中的相同,直接返回
    for (HandlerMethod current : oldList) {
        if (handlerMethod.equals(current)) {
            return;
        }
    }

    //否则重新创建一个list集合,将原来的处理器方法和现在的这个都添加进去
    List<HandlerMethod> newList = new ArrayList<>(oldList.size() + 1);
    newList.addAll(oldList);
    newList.add(handlerMethod);
    this.nameLookup.put(name, newList);
}

这个方法用来注册映射名,将命名策略生成的名字和处理器方法保存到注册表中

7.4 外部类AbstractHandlerMethodMapping

7.1章节的register(T mapping, Object handler, Method method)方法会调用很多外部类的方法,接下来我们就它外部类这些方法的源码

7.4.1 createHandlerMethod(Object handler, Method method)方法, 创建一个处理器方法对象

/**
 * Create the HandlerMethod instance.
 * @param handler either a bean name or an actual handler instance
 * @param method the target method
 * @return the created HandlerMethod
 */
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
    //传入工厂对象,在构造方法中通过getBean()方法得到handler对应的对象
    if (handler instanceof String) {
        return new HandlerMethod((String) handler,
                                 obtainApplicationContext().getAutowireCapableBeanFactory(), method);
    }
    return new HandlerMethod(handler, method);
}

这个方法将标注@RequestMapping注解的方法和方法所在类对象封装为一个处理器方法对象,方便处理器方法对象直接反射调用这个方法。

7.4.2 getNamingStrategy()方法,获取处理器方法映射命名策略

@Nullable
private HandlerMethodMappingNamingStrategy<T> namingStrategy;


/**
 * Return the configured naming strategy or {@code null}.
 */
@Nullable
public HandlerMethodMappingNamingStrategy<T> getNamingStrategy() {
    return this.namingStrategy;
}

RequestMappingHandlerMapping构造方法上初始化的命名策略类型为RequestMappingInfoHandlerMethodMappingNamingStrategy

7.4.3 initCorsConfiguration(Object handler, Method method, T mapping)方法, 解析方法和类上的@CrossOrigin注解,生成对应的跨域配置

/**
 * Extract and return the CORS configuration for the mapping.
 */
@Nullable
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, T mapping) {
   return null;
}

抽象方法,具体的逻辑由子类RequestMappingHandlerMapping实现

@Override
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
    //创建处理器方法对象,见7.4.1
    HandlerMethod handlerMethod = createHandlerMethod(handler, method);
    //获取处理器方法所在类的类型
    Class<?> beanType = handlerMethod.getBeanType();
    //获取类上的@CrossOrigin注解信息
    CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
    //获取方法上的@CrossOrigin注解信息
    CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);

    //类上和方法上都没有@CrossOrigin注解信息,就不创建跨域配置,直接返回null
    if (typeAnnotation == null && methodAnnotation == null) {
        return null;
    }

    //创建一个跨域配置
    CorsConfiguration config = new CorsConfiguration();
    //将类上的@CrossOrigin注解指定的跨域配置保存到跨域配置对象中,见7.4.3.1
    updateCorsConfig(config, typeAnnotation);
    //将方法上的@CrossOrigin注解指定的跨域配置保存到跨域配置对象中,见7.4.3.1
    updateCorsConfig(config, methodAnnotation);

    //用户未在@CrossOrigin注解上指定允许的请求方式
    if (CollectionUtils.isEmpty(config.getAllowedMethods())) {
        //将用户在@RequestMapping注解上配置的请求方式注册到跨域配置中
        for (RequestMethod allowedMethod : mappingInfo.getMethodsCondition().getMethods()) {
            //设置允许的请求方式
            config.addAllowedMethod(allowedMethod.name());
        }
    }
    //应用默认值,不会覆盖@CrossOrigin注解指定的配置
    return config.applyPermitDefaultValues();
}

该方法用来解析方法和类上的@CrossOrigin注解,并生成对应的跨域配置

7.4.3.1 将@CrossOrigin注解指定的跨域配置保存到跨域配置对象中
private void updateCorsConfig(CorsConfiguration config, @Nullable CrossOrigin annotation) {
    if (annotation == null) {
        return;
    }
    for (String origin : annotation.origins()) {
        config.addAllowedOrigin(resolveCorsAnnotationValue(origin));
    }
    //允许访问的请求方式
    for (RequestMethod method : annotation.methods()) {
        config.addAllowedMethod(method.name());
    }
    //允许请求的请求头列表
    for (String header : annotation.allowedHeaders()) {
        config.addAllowedHeader(resolveCorsAnnotationValue(header));
    }
    for (String header : annotation.exposedHeaders()) {
        config.addExposedHeader(resolveCorsAnnotationValue(header));
    }

    String allowCredentials = resolveCorsAnnotationValue(annotation.allowCredentials());
    if ("true".equalsIgnoreCase(allowCredentials)) {
        config.setAllowCredentials(true);
    }
    else if ("false".equalsIgnoreCase(allowCredentials)) {
        config.setAllowCredentials(false);
    }
    else if (!allowCredentials.isEmpty()) {
        throw new IllegalStateException("@CrossOrigin's allowCredentials value must be \"true\", \"false\", " +
                                        "or an empty string (\"\"): current value is [" + allowCredentials + "]");
    }

    if (annotation.maxAge() >= 0 && config.getMaxAge() == null) {
        config.setMaxAge(annotation.maxAge());
    }
}

解析@CrossOrigin注解的属性,并将属性值保存到跨域配置对象中

8 MappingRegistration

MappingRegistry类差不多,它是AbstractHandlerMethodMapping的一个嵌套类。它用来维护某个标注@RequestMapping注解方法的所有映射关系。

private static class MappingRegistration<T> {

    //RequestMappingInfo`
    private final T mapping;

    //处理器方法
    private final HandlerMethod handlerMethod;

    //对应的url路径
    private final List<String> directUrls;

    //映射名
    @Nullable
    private final String mappingName;

    public MappingRegistration(T mapping, HandlerMethod handlerMethod,
                               @Nullable List<String> directUrls, @Nullable String mappingName) {

        Assert.notNull(mapping, "Mapping must not be null");
        Assert.notNull(handlerMethod, "HandlerMethod must not be null");
        this.mapping = mapping;
        this.handlerMethod = handlerMethod;
        this.directUrls = (directUrls != null ? directUrls : Collections.emptyList());
        this.mappingName = mappingName;
    }

    public T getMapping() {
        return this.mapping;
    }

    public HandlerMethod getHandlerMethod() {
        return this.handlerMethod;
    }

    public List<String> getDirectUrls() {
        return this.directUrls;
    }

    @Nullable
    public String getMappingName() {
        return this.mappingName;
    }
}

它维护某个处理器方法的所有映射关系

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值