2023-03-14  阅读(0)
原文作者:lifullmoon 原文地址:https://www.cnblogs.com/lifullmoon

HandlerMapping 组件

HandlerMapping 组件,请求的 处理器匹配器 ,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors

  • handler 处理器是 Object 类型,可以将其理解成 HandlerMethod 对象(例如我们使用最多的 @RequestMapping 注解所标注的方法会解析成该对象),包含了方法的所有信息,通过该对象能够执行该方法
  • HandlerInterceptor 拦截器对处理请求进行 增强处理 ,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理

由于 HandlerMapping 组件涉及到的内容比较多,考虑到内容的排版,所以将这部分内容拆分成了四个模块,依次进行分析:

HandlerMapping 组件(三)之 AbstractHandlerMethodMapping

先来回顾一下HandlerMapping 接口体系的结构:

202303142234283001.png

《HandlerMapping 组件(一)之 AbstractHandlerMapping》 文档中已经分析了 HandlerMapping 组件的 AbstractHandlerMapping 抽象类基类

那么本文就接着来分析图中 红色框 部分的 AbstractHandlerMethodMapping 系,该系是基于 Method 进行匹配。例如,我们所熟知的 @RequestMapping 等注解的方式。一共就三个类,不多

涉及到的内容比较多,可以直接查看我的 总结

回顾

先来回顾一下在 DispatcherServlet 中处理请求的过程中通过 HandlerMapping 组件,获取到 HandlerExecutionChain 处理器执行链的方法,是通过AbstractHandlerMapping 的 getHandler 方法来获取的,如下:

    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // <1> 获得处理器(HandlerMethod 或者 HandlerExecutionChain),该方法是抽象方法,由子类实现
        Object handler = getHandlerInternal(request);
        // <2> 获得不到,则使用默认处理器
        // <3> 还是获得不到,则返回 null
        // <4> 如果找到的处理器是 String 类型,则从 Spring 容器中找到对应的 Bean 作为处理器
        // <5> 创建 HandlerExecutionChain 对象(包含处理器和拦截器)
        // ... 省略相关代码
        return executionChain;
    }

在 AbstractHandlerMapping 获取 HandlerExecutionChain 处理器执行链的方法中,需要先调用 getHandlerInternal(HttpServletRequest request) 抽象方法,获取请求对应的处理器,该方法由子类去实现,也就上图中 黄色框红色框 两类子类,本文分析 红色框 部分内容

注解

Spring MVC 的请求匹配的注解,体系结构如下:

202303142234315482.png

关于这些注解,大家已经非常熟悉了,各自的属性就不再进行讲述了,可具体查看源码:

AbstractHandlerMethodMapping

org.springframework.web.servlet.result.method.AbstractHandlerMethodMapping,实现 InitializingBean 接口,继承 AbstractHandlerMapping 抽象类,以 Method 方法 作为 Handler 处理器 的 HandlerMapping 抽象类,提供 Mapping 的初始化、注册等通用的骨架方法。

那么具体是什么呢?AbstractHandlerMethodMapping 定义为了 <T> 泛型,交给子类做决定。例如,子类 RequestMappingInfoHandlerMapping 使用 RequestMappingInfo 类作为 <T> 泛型,也就是我们在上面 注解 模块看到的 @RequestMapping 等注解信息。

构造方法

    public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    	/**
    	 * 是否只扫描可访问的 HandlerMethod 们
    	 */
    	private boolean detectHandlerMethodsInAncestorContexts = false;
    	/**
    	 * Mapping 命名策略
    	 */
    	@Nullable
    	private HandlerMethodMappingNamingStrategy<T> namingStrategy;
    	/**
         * Mapping 注册表
         */
    	private final MappingRegistry mappingRegistry = new MappingRegistry();
    }
  • <T> 泛型,就是我们前面要一直提到的 Mapping 类型
  • mappingRegistry:Mapping 注册表,详细见下文
  • namingStrategyorg.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy 接口,HandlerMethod Mapping 名字生成策略接口
        @FunctionalInterface
        public interface HandlerMethodMappingNamingStrategy<T> {
        	/**
        	 * 根据 HandlerMethod 获取名称,就是为对应的 Mappring 对象生成一个名称,便于获取
        	 */
        	String getName(HandlerMethod handlerMethod, T mapping);
        }
        // org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMethodMappingNamingStrateg.java
        public class RequestMappingInfoHandlerMethodMappingNamingStrategy implements HandlerMethodMappingNamingStrategy<RequestMappingInfo> {
        
        	/** Separator between the type and method-level parts of a HandlerMethod mapping name. */
        	public static final String SEPARATOR = "#";
        
        	@Override
        	public String getName(HandlerMethod handlerMethod, RequestMappingInfo mapping) {
        		// 情况一,mapping 名字非空,则使用 mapping 的名字
        		if (mapping.getName() != null) {
        			return mapping.getName();
        		}
        		// 情况二,使用类名大写 + "#" + 方法名
        		StringBuilder sb = new StringBuilder();
        		String simpleTypeName = handlerMethod.getBeanType().getSimpleName();
        		for (int i = 0; i < simpleTypeName.length(); i++) {
        			if (Character.isUpperCase(simpleTypeName.charAt(i))) {
        				sb.append(simpleTypeName.charAt(i));
        			}
        		}
        		sb.append(SEPARATOR).append(handlerMethod.getMethod().getName());
        		return sb.toString();
        	}
        
        }
 *  情况一,如果 Mapping 已经配置名字,则直接返回。例如,`@RequestMapping(name = "login", value = "user/login")` 注解的方法,它对应的 Mapping 的名字就是 `login`
 *  情况二,如果 Mapping 未配置名字,则使用使用类名大写 + `"#"` \+ 方法名。例如,`@RequestMapping(value = "user/login")` 注解的方法,假设它所在的类为 UserController ,对应的方法名为 login ,则它对应的 Mapping 的名字就是 `USERCONTROLLER#login`

MappingRegistry 注册表

AbstractHandlerMethodMapping 的内部类,Mapping 注册表

构造方法
    class MappingRegistry {
        /**
         * 注册表
         *
         * Key: Mapping
         * Value:{@link MappingRegistration}(Mapping + HandlerMethod)
         */
        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
        /**
         * 注册表2
         *
         * Key:Mapping
         * Value:{@link HandlerMethod}
         */
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
        /**
         * 直接 URL 的映射
         *
         * Key:直接 URL(就是固定死的路径,而非多个)
         * Value:Mapping 数组
         */
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
        /**
         * Mapping 的名字与 HandlerMethod 的映射
         *
         * Key:Mapping 的名字
         * Value:HandlerMethod 数组
         */
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
    
        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
        /**
         * 读写锁
         */
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    }
  • registry:注册表。 Key : Mapping,即 <T> 泛型; Value :MappingRegistration 对象(Mapping + HandlerMethod)
  • mappingLookup:注册表2。 Key : Mapping,即 <T> 泛型; Value :HandlerMethod 对象
  • urlLookup:直接 URL 的映射。 Key :直接 URL(就是固定死的路径,而非多个); Value :Mapping 数组
  • nameLookup:Mapping 的名字与 HandlerMethod 的映射。 Key :Mapping 的名字; Value :HandlerMethod 数组
  • readWriteLock:读写锁,为了才操作上述属性时保证线程安全
register

register(T mapping, Object handler, Method method)方法,将 Mapping、Method、handler(方法所在类)之间的映射关系进行注册,会生成 HandlerMethod 对象,就是处理器对象,方法如下:

    public void register(T mapping, Object handler, Method method) {
        // <1> 获得写锁
        this.readWriteLock.writeLock().lock();
        try {
            // <2.1> 创建 HandlerMethod 对象
            HandlerMethod handlerMethod = createHandlerMethod(handler, method);
            // <2.2> 校验当前 mapping 是否存在对应的 HandlerMethod 对象,如果已存在但不是当前的 handlerMethod 对象则抛出异常
            assertUniqueMethodMapping(handlerMethod, mapping);
            // <2.3> 将 mapping 与 handlerMethod 的映射关系保存至 this.mappingLookup
            this.mappingLookup.put(mapping, handlerMethod);
    
            // <3.1> 获得 mapping 对应的普通 URL 数组
            List<String> directUrls = getDirectUrls(mapping);
            // <3.2> 将 url 和 mapping 的映射关系保存至 this.urlLookup
            for (String url : directUrls) {
                this.urlLookup.add(url, mapping);
            }
    
            // <4> 初始化 nameLookup
            String name = null;
            if (getNamingStrategy() != null) {
                // <4.1> 获得 Mapping 的名字
                name = getNamingStrategy().getName(handlerMethod, mapping);
                // <4.2> 将 mapping 的名字与 HandlerMethod 的映射关系保存至 this.nameLookup
                addMappingName(name, handlerMethod);
            }
    
            // <5> 初始化 CorsConfiguration 配置对象
            CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
            if (corsConfig != null) {
                this.corsLookup.put(handlerMethod, corsConfig);
            }
            // <6> 创建 MappingRegistration 对象
            // 并与 mapping 映射添加到 registry 注册表中
            this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
        }
        finally {
            // <7> 释放写锁
            this.readWriteLock.writeLock().unlock();
        }
    }
  1. 获得写锁

  2. 添加相关映射至Map<T, HandlerMethod> mappingLookup属性

    1. 调用 createHandlerMethod(Object handler, Method method) 方法,创建 HandlerMethod 对象,详情见下文
    2. 校验当前 Mapping 是否存在对应的 HandlerMethod 对象,如果已存在但不是前一步创建 HandlerMethod 对象则抛出异常,保证唯一性
    3. 将 Mapping 与 HandlerMethod 的映射关系保存至 mappingLookup
  3. 添加相关映射至MultiValueMap<String, T> urlLookup属性

    1. 调用 getDirectUrls 方法,获得 Mapping 对应的直接 URL 数组,如下:
            private List<String> getDirectUrls(T mapping) {
                List<String> urls = new ArrayList<>(1);
                // 遍历 Mapping 对应的路径
                for (String path : getMappingPathPatterns(mapping)) {
                    // 非**模式**路径
                    if (!getPathMatcher().isPattern(path)) {
                        urls.add(path);
                    }
                }
                return urls;
            }
     *  例如,`@RequestMapping("/user/login")` 注解对应的路径, **就是** 直接路径
     *  例如,`@RequestMapping("/user/${id}")` 注解对应的路径, **不是** 直接路径,因为不确定性
2.  将 url 和 Mapping 的映射关系保存至 `urlLookup`
  1. 添加相关映射至Map<String, List<HandlerMethod>> nameLookup属性

    1. 调用 HandlerMethodMappingNamingStrategy#getName(HandlerMethod handlerMethod, T mapping) 方法,获得 Mapping 的名字
    2. 调用 addMappingName(String name, HandlerMethod handlerMethod) 方法,添加 Mapping 的名字 + HandlerMethod 至 nameLookup,如下:
            private void addMappingName(String name, HandlerMethod handlerMethod) {
                // 获得 Mapping 的名字,对应的 HandlerMethod 数组
                List<HandlerMethod> oldList = this.nameLookup.get(name);
                if (oldList == null) {
                    oldList = Collections.emptyList();
                }
                // 如果已经存在,则不用添加
                for (HandlerMethod current : oldList) {
                    if (handlerMethod.equals(current)) {
                        return;
                    }
                }
                // 添加到 nameLookup 中
                List<HandlerMethod> newList = new ArrayList<>(oldList.size() + 1);
                newList.addAll(oldList);
                newList.add(handlerMethod);
                this.nameLookup.put(name, newList);
            }
    和已有的进行合并,说明名称不是唯一哦
  1. 初始化 CorsConfiguration 配置对象,暂时忽略
  2. 创建 MappingRegistration 对象,并和 Mapping 进行映射添加至 registry
  3. 释放写锁
unregister

unregister(T mapping) 方法,取消上面方法注册的相关信息,方法如下:

    public void unregister(T mapping) {
    // 获得写锁
    this.readWriteLock.writeLock().lock();
    try {
        // 从 registry 中移除
        MappingRegistration<T> definition = this.registry.remove(mapping);
        if (definition == null) {
            return;
        }
    
        // 从 mappingLookup 中移除
        this.mappingLookup.remove(definition.getMapping());
    
        // 从 urlLookup 移除
        for (String url : definition.getDirectUrls()) {
            List<T> list = this.urlLookup.get(url);
            if (list != null) {
                list.remove(definition.getMapping());
                if (list.isEmpty()) {
                    this.urlLookup.remove(url);
                }
            }
        }
    
        // 从 nameLookup 移除
        removeMappingName(definition);
    
        // 从 corsLookup 中移除
        this.corsLookup.remove(definition.getHandlerMethod());
    }
    finally {
        // 释放写锁
        this.readWriteLock.writeLock().unlock();
    }

register 方法逻辑相反,依次移除相关映射

createHandlerMethod

createHandlerMethod(Object handler, Method method)方法,创建 Method 对应的 HandlerMethod 对象

    protected HandlerMethod createHandlerMethod(Object handler, Method method) {
        HandlerMethod handlerMethod;
        // <1> 如果 handler 类型为 String, 说明对应一个 Bean 对象的名称
        // 例如 UserController 使用 @Controller 注解后,默认入参 handler 就是它的 beanName ,即 `userController`
        if (handler instanceof String) {
            String beanName = (String) handler;
            handlerMethod = new HandlerMethod(beanName, obtainApplicationContext().getAutowireCapableBeanFactory(), method);
        }
        // <2> 如果 handler 类型非 String ,说明是一个已经是一个 handler 对象,就无需处理,直接创建 HandlerMethod 对象
        else {
            handlerMethod = new HandlerMethod(handler, method);
        }
        return handlerMethod;
    }
  1. 如果 handler 类型为 String, 说明对应一个 Bean 对象的名称。例如 UserController 使用 @Controller 注解后,默认入参 handler 就是它的 beanName ,即 userController
  2. 如果 handler 类型非 String ,说明是一个已经是一个 handler 对象,就无需处理,直接创建 HandlerMethod 对象

所以你会发现 HandlerMethod 处理器对象,就是handler(方法所在类)+method(方法对象)的组合,通过它能执行该方法

HandlerMethod 处理器

org.springframework.web.method.HandlerMethod,处理器对象,也就是某个方法的封装对象(Method+所在类的 Bean 对象),有以下属性:

    public class HandlerMethod {
    	/**
    	 * Bean 对象
    	 */
    	private final Object bean;
    	@Nullable
    	private final BeanFactory beanFactory;
    	/**
    	 * Bean 的类型
    	 */
    	private final Class<?> beanType;
    	/**
    	 * 方法对象
    	 */
    	private final Method method;
    	/**
    	 * {@link #method} 的桥接方法
    	 * 存在泛型类型,编译器则会自动生成一个桥接方法(java1.5向后兼容)
    	 */
    	private final Method bridgedMethod;
    	/**
    	 * 方法的参数类型数组
    	 */
    	private final MethodParameter[] parameters;
    	/**
    	 * 响应的状态码,即 {@link ResponseStatus#code()}
    	 */
    	@Nullable
    	private HttpStatus responseStatus;
    	/**
    	 * 响应的状态码原因,即 {@link ResponseStatus#reason()}
    	 */
    	@Nullable
    	private String responseStatusReason;
    	/**
    	 * 解析自哪个 HandlerMethod 对象
    	 *
    	 * 仅构造方法中传入 HandlerMethod 类型的参数适用,例如 {@link #HandlerMethod(HandlerMethod)}
    	 */
    	@Nullable
    	private HandlerMethod resolvedFromHandlerMethod;
    	/**
    	 * 父接口的方法的参数注解数组
    	 */
    	@Nullable
    	private volatile List<Annotation[][]> interfaceParameterAnnotations;
    }

根据上面的注释理解上面的属性,包含该方法的所有信息

它的构造函数非常多,不过原理都差不多,我们挑两个来看看

HandlerMethod(String beanName, BeanFactory beanFactory, Method method) 构造方法

对应 createHandlerMethod(Object handler, Method method)方法的 <1>,代码如下:

    public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
        Assert.hasText(beanName, "Bean name is required");
        Assert.notNull(beanFactory, "BeanFactory is required");
        Assert.notNull(method, "Method is required");
        // <1> 将 beanName 赋值给 bean 属性,说明 beanFactory + bean 的方式,获得 handler 对象
        this.bean = beanName;
        this.beanFactory = beanFactory;
        // <2> 初始化 beanType 属性
        Class<?> beanType = beanFactory.getType(beanName);
        if (beanType == null) {
            throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
        }
        this.beanType = ClassUtils.getUserClass(beanType);
        // <3> 初始化 method、bridgedMethod 属性
        this.method = method;
        // 如果不是桥接方法则直接为该方法
        this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
        // <4> 初始化 parameters 属性,解析该方法(或者桥接方法)的参数类型
        this.parameters = initMethodParameters();
        // <5> 初始化 responseStatus、responseStatusReason 属性,通过 @ResponseStatus 注解
        evaluateResponseStatus();
    }
  1. beanName 赋值给 bean 属性,说明 beanFactory + bean 的方式,获得 handler 对象
  2. 初始化 beanType 属性
  3. 初始化 methodbridgedMethod 属性
  4. 初始化 parameters 属性,解析该方法(或者桥接方法)的参数类型,调用 initMethodParameters() 方法,如下:
        private MethodParameter[] initMethodParameters() {
            int count = this.bridgedMethod.getParameterCount();
            // 创建 MethodParameter 数组
            MethodParameter[] result = new MethodParameter[count];
            // 遍历 bridgedMethod 方法的参数,逐个解析它的参数类型
            for (int i = 0; i < count; i++) {
                HandlerMethodParameter parameter = new HandlerMethodParameter(i);
                GenericTypeResolver.resolveParameterType(parameter, this.beanType);
                result[i] = parameter;
            }
            return result;
        }
  1. 初始化 responseStatusresponseStatusReason 属性,通过 @ResponseStatus 注解

关于桥接方法呢,是 java1.5 引入泛型向后兼容的一种方法,具体可参考Effects of Type Erasure and Bridge Methods

HandlerMethod(Object bean, Method method) 构造方法

对应 createHandlerMethod(Object handler, Method method)方法的 <2>,代码如下:

    public HandlerMethod(Object bean, Method method) {
        Assert.notNull(bean, "Bean is required");
        Assert.notNull(method, "Method is required");
        // <1> 初始化 Bean
        this.bean = bean;
        this.beanFactory = null;
        // <2> 初始化 beanType 属性
        this.beanType = ClassUtils.getUserClass(bean);
        // <3> 初始化 method、bridgedMethod 属性
        this.method = method;
        // 如果不是桥接方法则直接为该方法
        this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
        // <4> 初始化 parameters 属性,解析该方法(或者桥接方法)的参数类型
        this.parameters = initMethodParameters();
        // <5> 初始化 responseStatus、responseStatusReason 属性,通过 @ResponseStatus 注解
        evaluateResponseStatus();
    }

和上面的构造方法差不多,不同的是这里的 bean 对象就是方法所在类的 Bean 对象

MappingRegistration 注册登记

AbstractHandlerMethodMapping 的私有静态内部类,Mapping 的注册登记信息,包含 Mapiing、HandlerMethod、直接 URL 路径、Mapping 名称,代码如下:

    private static class MappingRegistration<T> {
        /**
         * Mapping 对象
         */
        private final T mapping;
        /**
         * HandlerMethod 对象
         */
        private final HandlerMethod handlerMethod;
        /**
         * 直接 URL 数组(就是固定死的路径,而非多个)
         */
        private final List<String> directUrls;
        /**
         * {@link #mapping} 的名字
         */
        @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;
        }
    }

很简单,就是保存了 Mapping 注册时的一些信息

1.afterPropertiesSet 初始化方法

因为 AbstractHandlerMethodMapping 实现了 InitializingBean 接口,在 Sping 初始化该 Bean 的时候,会调用该方法,完成一些初始化工作,方法如下:

    @Override
    public void afterPropertiesSet() {
        // <x> 初始化处理器的方法们
        initHandlerMethods();
    }
    
    protected void initHandlerMethods() {
        // <1> 遍历 Bean ,逐个处理
        for (String beanName : getCandidateBeanNames()) {
            // 排除目标代理类,AOP 相关,可查看注释
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                // <2> 处理 Bean
                processCandidateBean(beanName);
            }
        }
        // <3> 初始化处理器的方法们,目前是空方法,暂无具体的实现
        handlerMethodsInitialized(getHandlerMethods());
    }
  1. 调用 getCandidateBeanNames() 方法,获取到 Spring 上下文中所有为 Object 类型的 Bean 的名称集合,然后进行遍历,方法如下:
        protected String[] getCandidateBeanNames() {
            // 获取上下文中所有的 Bean 的名称
            return (this.detectHandlerMethodsInAncestorContexts ?
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class)
                    : obtainApplicationContext().getBeanNamesForType(Object.class));
        }
`detectHandlerMethodsInAncestorContexts`:是否只扫描可访问的 HandlerMethod 们,默认`false`
  1. 调用 processCandidateBean(String beanName) 方法,处理每个符合条件的 Bean 对象(例如有 @Controller 或者 @RequestMapping 注解的 Bean),解析这些 Bean 下面相应的方法,往 MappingRegistry 注册,详情见下文
  2. 调用 handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) 方法,初始化 HandlerMethod 处理器们,空方法,暂无子类实现

2.processCandidateBean

processCandidateBean(String beanName)方法,处理符合条件的 Bean 对象,解析其相应的方法,往 MappingRegistry 注册,方法如下:

    protected void processCandidateBean(String beanName) {
        // <1> 获得 Bean 对应的 Class 对象
        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);
            }
        }
        // <2> 判断 Bean 是否为处理器(例如有 @Controller 或者 @RequestMapping 注解)
        if (beanType != null && isHandler(beanType)) {
            // <3> 扫描处理器方法
            detectHandlerMethods(beanName);
        }
    }
  1. 获得 Bean 对应的 Class 对象
  2. 调用 isHandler(Class<?> beanType) 抽象 方法,判断 Bean 的类型是否需要处理,其 RequestMappingHandlerMapping 子类的实现:有 @Controller 或者 @RequestMapping 注解的 Bean
  3. 调用 detectHandlerMethods(Object handler) 方法,扫描 Bean 的方法进行处理

3.detectHandlerMethods

detectHandlerMethods(Object handler)方法,初始化 Bean 下面的方法们为 HandlerMethod 对象,并注册到 MappingRegistry 注册表中,代码如下:

    protected void detectHandlerMethods(Object handler) {
        // <1> 获得 Bean 对应的 Class 对象
        Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());
        if (handlerType != null) {
            // <2> 获得真实的 Class 对象,因为 `handlerType` 可能是代理类
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            // <3> 获得匹配的方法和对应的 Mapping 对象
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            // 创建该方法对应的 Mapping 对象,例如根据 @RequestMapping 注解创建 RequestMappingInfo 对象
                            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));
            }
            // <4> 遍历方法,逐个注册 HandlerMethod
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
  1. 获得 Bean 对应的 Class 对象 handlerType
  2. 调用getUserClass(Class<?> clazz)方法,获得真实的 Class 对象,因为 handlerType 可能是代理类,如下:
        public static Class<?> getUserClass(Class<?> clazz) {
            // 如果 Class 对象的名称包含 "$$",则是 CG_CLASS 代理类,则获取其父类
            if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
                Class<?> superclass = clazz.getSuperclass();
                if (superclass != null && superclass != Object.class) {
                    return superclass;
                }
            }
            return clazz;
        }
  1. 获得匹配的方法和对应的 Mapping 对象,其中泛型 T,也就是 Mapping 对象,需要通过 getMappingForMethod(Method method, Class<?> handlerType) 抽象方法返回,其 RequestMappingHandlerMapping 子类的实现,根据 @RequestMapping 注解为方法创建 RequestMappingInfo 对象

    所以这里的 Mapping 对象就是 RequestMappingInfo 对象

  2. 遍历方法(方法与 Mapping 一一映射了),调用registerHandlerMethod(Object handler, Method method, T mapping),逐个注册对应的 HandlerMethod 对象,如下:

        protected void registerHandlerMethod(Object handler, Method method, T mapping) {
            this.mappingRegistry.register(mapping, handler, method);
        }
也就是上面 MappingRegistry 的 register 方法,已经分析过了

到这里我们已经分析完了 AbstractHandlerMethodMapping 的初始化工作,部分细节在子类中实现

大致逻辑:扫描有 @Controller 或者 @RequestMapping 注解的类下面的方法,如果方法上面有 @RequestMapping 注解,则会为该方法创建对应的 RequestMappingInfo 对象

将所有的 RequestMappingInfo 对象和 Method 以及方法所在类,往 MappingRegistry 进行注册,会生成 HandlerMethod 处理器(Method 所有信息)对象

这样一来,当 Spring MVC 的 DispatcherServlet 处理请求的时候,获取到对应的 HandlerMethod 处理器,就可以通过反射执行对应的方法了

到这里,思路是不是越来越清晰了,我们继续往下分析

【重点】getHandlerInternal

由于上面初始化涉及到内容有点多,先回到本文上面的 回顾 这一小节,通过 AbstractHandlerMappinggetHandler(HttpServletRequest request) 方法获取 HandlerExecutionChain 处理器执行链时,需要调用 getHandlerInternal 抽象 方法获取处理器,这个方法由子类去实现,就到这里了

getHandlerInternal(ServerWebExchange exchange)方法,获得请求对应的 HandlerMethod 处理器对象,方法如下:

    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // <1> 获得请求的路径
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        // <2> 获得读锁
        this.mappingRegistry.acquireReadLock();
        try {
            // <3> 获得 HandlerMethod 对象
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            // <4> 进一步,获得一个新的 HandlerMethod 对象
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            // <5> 释放读锁
            this.mappingRegistry.releaseReadLock();
        }
    }
  1. 获得请求路径
  2. 获得读锁
  3. 调用 lookupHandlerMethod(ServerWebExchange exchange) 方法,获得请求对应的 HandlerMethod 处理器对象,详情见下文
  4. 如果获得到 HandlerMethod 对象,则调用 HandlerMethod#createWithResolvedBean() 方法,进一步,获得 HandlerMethod 对象,如下:
        public HandlerMethod createWithResolvedBean() {
            Object handler = this.bean;
            // 如果是 bean 是 String 类型,则获取对应的 Bean,因为创建该对象时 bean 可能是对应的 beanName
            if (this.bean instanceof String) {
                Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
                String beanName = (String) this.bean;
                handler = this.beanFactory.getBean(beanName);
            }
            return new HandlerMethod(this, handler);
        }
  1. 释放读锁

lookupHandlerMethod 获取处理器方法

lookupHandlerMethod(ServerWebExchange exchange)方法,获得请求对应的 HandlerMethod 处理器对象,方法如下:

    @Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        // <1> Match 数组,存储匹配上当前请求的结果(Mapping + HandlerMethod)
        List<Match> matches = new ArrayList<>();
        // <1.1> 优先,基于直接 URL (就是固定死的路径,而非多个)的 Mapping 们,进行匹配
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        // <1.2> 其次,扫描注册表的 Mapping 们,进行匹配
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }
    
        // <2> 如果匹配到,则获取最佳匹配的 Match 结果的 `HandlerMethod`属性
        if (!matches.isEmpty()) {
            // <2.1> 创建 MatchComparator 对象,排序 matches 结果,排序器
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            matches.sort(comparator);
            // <2.2> 获得首个 Match 对象,也就是最匹配的
            Match bestMatch = matches.get(0);
            // <2.3> 处理存在多个 Match 对象的情况!!
            if (matches.size() > 1) {
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                // 比较 bestMatch 和 secondBestMatch ,如果相等,说明有问题,抛出 IllegalStateException 异常
                // 因为,两个优先级一样高,说明无法判断谁更优先
                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);
            // <2.4> 处理首个 Match 对象
            handleMatch(bestMatch.mapping, lookupPath, request);
            // <2.5> 返回首个 Match 对象的 handlerMethod 属性
            return bestMatch.handlerMethod;
        }
        // <3> 如果匹配不到,则处理不匹配的情况
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }
  1. 定义 Match 数组 matches,存储匹配上当前请求的结果(Mapping + HandlerMethod

    1. 优先,基于直接 URL (就是固定死的路径,而非多个)的 Mapping 们,进行匹配
    2. 其次,扫描注册表的 Mapping 们,进行匹配

    上述的1.11.2,都会调用addMatchingMappings(Collection<T> mappings, List<Match> matches, ServerWebExchange exchange) 方法

    将当前请求和注册表中的 Mapping 进行匹配,匹配成功则生成匹配结果 Match,添加到 matches 中,方法如下:

        private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
            // 遍历 Mapping 数组
            for (T mapping : mappings) {
                // <1> 执行匹配,抽象方法,交由子类实现
                T match = getMatchingMapping(mapping, request);
                if (match != null) {
                    // <2> 如果匹配,则创建 Match 对象,添加到 matches 中
                        matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
                }
            }
        }
  1. 如果匹配到,则获取最佳匹配的 Match 结果的 HandlerMethod 属性

    1. 创建 MatchComparator 对象,排序 matches 的结果,排序器
    2. 获得首个 Match 结果,也就是 最匹配的
    3. 处理存在多个 Match 的情况,则判断第二匹配的和最匹配的是否“相同”,是的话就抛出异常
    4. 处理 最匹配 的 Match,设置请求路径 lookupPath 到请求属性
    5. 返回 最匹配 的 Match 的 HandlerMethod 处理器对象
  2. 如果匹配不到,则处理不匹配的情况,调用handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)方法,这里返回null

到这里 AbstractHandlerMethodMapping 抽象类差不多全部分析完了,其中有几个 抽象方法 交由子类去实现

  • protected abstract boolean isHandler(Class<?> beanType);
  • protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
  • protected abstract Set<String> getMappingPathPatterns(T mapping);
  • protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
  • protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);

RequestMappingInfoHandlerMapping

org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping,继承 AbstractHandlerMethodMapping 抽象类,定义了使用的泛型 <T>org.springframework.web.servlet.mvc.method.RequestMappingInfo 类,即 Mapping 类型就是 RequestMappingInfo 对象

这样有什么好处呢

RequestMappingInfoHandlerMapping 定义了使用 RequestMappingInfo 对象,而其子类 RequestMappingHandlerMapping 将使用了 @RequestMapping 注解的方法,解析生成 RequestMappingInfo 对象。这样,如果未来我们自己定义注解,或者其他方式来生成 RequestMappingHandlerMapping 对象,未尝不可。

构造方法

    public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
        protected RequestMappingInfoHandlerMapping() {
            // 设置父类的 namingStrategy 属性 Mapping 命名策略对象,为 RequestMappingInfoHandlerMethodMappingNamingStrategy 对象
            setHandlerMethodMappingNamingStrategy(new RequestMappingInfoHandlerMethodMappingNamingStrategy());
        }
    }
  • <T> 泛型,为 RequestMappingInfo 类型

  • 设置父类 AbstractHandlerMethodMappingnamingStrategy 属性为 RequestMappingInfoHandlerMethodMappingNamingStrategy 对象

    是否还记得这个为 Mapping 生成名称的类?在 AbstractHandlerMethodMapping 中进行分析过了

RequestMappingInfo 对象

RequestMappingInfo 不是 RequestMappingInfoHandlerMapping 的内部类,而是 RequestMappingInfo HandlerMapping 的 前缀

org.springframework.web.servlet.mvc.method.RequestMappingInfo,实现 RequestCondition 接口,每个方法的定义的请求信息,也就是 @RequestMapping 等注解的信息

关于 org.springframework.web.servlet.mvc.condition.RequestCondition,条件接口,定义了三个方法,分别是:

  • combine(T other),合并方法
  • getMatchingCondition(HttpServletRequest request),匹配方法
  • compareTo(T other, HttpServletRequest request),比较方法
构造方法
    public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
    	/**
    	 * 名字
    	 */
    	@Nullable
    	private final String name;
    	/**
    	 * 请求路径的条件
    	 */
    	private final PatternsRequestCondition patternsCondition;
    	/**
    	 * 请求方法的条件
    	 */
    	private final RequestMethodsRequestCondition methodsCondition;
    	/**
    	 * 请求参数的条件
    	 */
    	private final ParamsRequestCondition paramsCondition;
    	/**
    	 * 请求头的条件
    	 */
    	private final HeadersRequestCondition headersCondition;
    	/**
    	 * 可消费的 Content-Type 的条件
    	 */
    	private final ConsumesRequestCondition consumesCondition;
    	/**
    	 * 可生产的 Content-Type 的条件
    	 */
    	private final ProducesRequestCondition producesCondition;
    	/**
    	 * 自定义的条件
    	 */
    	private final RequestConditionHolder customConditionHolder;
    }
  • 可以看到属性中有各种条件。实际上,和 @RequestMapping 注解是一一对应的。所以,每个属性的详细解释,相信你经常使用到
  • 实际上,我们日常使用最多的还是 patternsCondition 请求路径条件,和 methodsCondition 请求方法条件

RequestCondition 接口体系结构如下:

202303142234363153.png

getMatchingCondition

getMatchingCondition(HttpServletRequest request)方法,从当前 RequestMappingInfo 获得匹配的条件。如果匹配,则基于其匹配的条件,创建 新的 RequestMappingInfo 对象,如果不匹配,则返回 null ,代码如下:

    @Override
    @Nullable
    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
        // 匹配 methodsCondition、paramsCondition、headersCondition、consumesCondition、producesCondition
        // 如果任一为空,则返回 null ,表示匹配失败
        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;
        }
        /*
         * 创建匹配的 RequestMappingInfo 对象
         * 为什么要创建 RequestMappingInfo 对象呢?
         *
         * 因为当前 RequestMappingInfo 对象,一个 methodsCondition 可以配置 GET、POST、DELETE 等等条件,
         * 但是实际就匹配一个请求类型,此时 methods 只代表其匹配的那个。
         */
        return new RequestMappingInfo(this.name, patterns,
                methods, params, headers, consumes, produces, custom.getCondition());
    }
  • 虽然代码非常长,实际都是调用每个属性对应的 getMatchingCondition(HttpServletRequest request) 方法,获得其匹配的 真正的 条件

可能你会疑惑,如果一个 @RequestMapping(value = "user/login") 注解,并未写 RequestMethod 的条件,岂不是会报空?

实际上不会。在这种情况下,会创建一个 RequestMethodsRequestCondition 对象,并且在匹配时,直接返回自身,代码如下:

    @Override
    @Nullable
    public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
        if (CorsUtils.isPreFlightRequest(request)) {
            return matchPreFlight(request);
        }
        // 空的情况下,就返回自身
        if (getMethods().isEmpty()) {
            if (RequestMethod.OPTIONS.name().equals(request.getMethod()) &&
                    !DispatcherType.ERROR.equals(request.getDispatcherType())) {
                return null; // No implicit match for OPTIONS (we handle it)
            }
            return this;
        }
        // 非空,逐个匹配
        return matchRequestMethod(request.getMethod());
    }

也就是说,没有 RequestMethod 的条件,则一定匹配成功,且结果就是自身 RequestMethodsRequestCondition 对象

总结 :就是根据配置的 @RequestMapping 注解,如果所有条件都满足,则创建一个 RequestMappingInfo 对象返回,如果某个条件不满足则直接返回 null,表示不匹配

compareTo

compareTo(RequestMappingInfo other, HttpServletRequest request)方法,比较优先级,方法如下:

    @Override
    public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
        int result;
        // Automatic vs explicit HTTP HEAD mapping
        // 针对 HEAD 请求方法,特殊处理
        if (HttpMethod.HEAD.matches(request.getMethod())) {
            result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
            if (result != 0) {
                return result;
            }
        }
        /*
         * 依次比较 patternsCondition、paramsCondition、headersCondition、consumesCondition、
         * producesCondition、methodsCondition、customConditionHolder
         * 如果有一个不相等,则直接返回比较结果
         */
        result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.producesCondition.compareTo(other.getProducesCondition(), request);
        if (result != 0) {
            return result;
        }
        // Implicit (no method) vs explicit HTTP method mappings
        result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
        if (result != 0) {
            return result;
        }
        return 0;
    }

关于各种 RequestCondition 请求条件就不一一分析了

getMappingPathPatterns

getMappingPathPatterns(RequestMappingInfo info)方法,获得 RequestMappingInfo 对应的请求路径集合,代码如下:

    @Override
    protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
    	return info.getPatternsCondition().getPatterns();
    }
  • 在 MappingRegistry 注册表的 register 方法中的第 3 步会调用,将所有符合的请求路径与该 RequestMappingInfo 对象进行映射保存

getMatchingMapping

getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) 方法,判断请求是否匹配入参 RequestMappingInfo 对象,代码如下:

    @Override
    protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
        return info.getMatchingCondition(request);
    }
  • 在 AbstractHandlerMethodMapping 的 lookupHandlerMethod 获取处理器方法的<1.1><1.2>会调用,遍历所有的 Mapping 对象,获取到该请求所匹配的 RequestMappingInfo 对象

handleMatch

handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request)方法,覆写父类的方法,设置更多的属性到请求中,代码如下:

    @Override
    protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
        super.handleMatch(info, lookupPath, request);
    
        // 获得 bestPattern 和 uriVariables
        String bestPattern; // 最佳路径
        Map<String, String> uriVariables; // 路径上的变量集合
    
        Set<String> patterns = info.getPatternsCondition().getPatterns();
        if (patterns.isEmpty()) {
            bestPattern = lookupPath;
            uriVariables = Collections.emptyMap();
        }
        else {
            bestPattern = patterns.iterator().next();
            uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
        }
    
        request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
    
        // 设置 MATRIX_VARIABLES_ATTRIBUTE 属性,到请求中
        if (isMatrixVariableContentAvailable()) {
            Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
            request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
        }
    
        // 设置 URI_TEMPLATE_VARIABLES_ATTRIBUTE 属性,到请求中
        Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
        request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
    
        // 设置 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性,到请求中
        if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
            Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
            request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
        }
    }

具体用途还不清楚

handleNoMatch

handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) 方法,覆写父类方法,处理无匹配 Mapping 的情况

主要用途是,给出为什么找不到 Mapping 的原因,代码如下:

    @Override
    protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {
    
        // <1> 创建 PartialMatchHelper 对象,解析可能的错误
        PartialMatchHelper helper = new PartialMatchHelper(infos, request);
        if (helper.isEmpty()) {
            return null;
        }
    
        // <2> 方法错误
        if (helper.hasMethodsMismatch()) {
            Set<String> methods = helper.getAllowedMethods();
            if (HttpMethod.OPTIONS.matches(request.getMethod())) {
                HttpOptionsHandler handler = new HttpOptionsHandler(methods);
                return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
            }
            throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
        }
    
        // <3> 可消费的 Content-Type 错误
        if (helper.hasConsumesMismatch()) {
            Set<MediaType> mediaTypes = helper.getConsumableMediaTypes();
            MediaType contentType = null;
            if (StringUtils.hasLength(request.getContentType())) {
                try {
                    contentType = MediaType.parseMediaType(request.getContentType());
                }
                catch (InvalidMediaTypeException ex) {
                    throw new HttpMediaTypeNotSupportedException(ex.getMessage());
                }
            }
            throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
        }
    
        // <4> 可生产的 Content-Type 错误
        if (helper.hasProducesMismatch()) {
            Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
            throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
        }
    
        // <5> 参数错误
        if (helper.hasParamsMismatch()) {
            List<String[]> conditions = helper.getParamConditions();
            throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
        }
    
        return null;
    }
  1. 核心代码在 PartialMatchHelper 中实现,暂时忽略
  2. 方法错误。这是一个非常常见的错误,例如说 POST user/login 存在,但是我们请求了 GET user/login
  3. 可消费的 Content-Type 错误
  4. 可生产的 Content-Type 错误
  5. 参数错误

RequestMappingHandlerMapping

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,实现 MatchableHandlerMapping、EmbeddedValueResolverAware 接口,继承 RequestMappingInfoHandlerMapping 抽象类,基于@RequestMapping 注解来构建 RequestMappingInfo 对象

写到这里有那么一点点感动,终于到最底层的实现类了

构造方法

    public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
    		implements MatchableHandlerMapping, EmbeddedValueResolverAware {
    
    	private boolean useSuffixPatternMatch = true;
    
    	private boolean useRegisteredSuffixPatternMatch = false;
    
    	private boolean useTrailingSlashMatch = true;
    
    	private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
    
    	private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
    
    	@Nullable
    	private StringValueResolver embeddedValueResolver;
    
        /**
    	 * RequestMappingInfo 的构建器
    	 */
    	private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
    }

afterPropertiesSet

因为父类 AbstractHandlerMethodMapping 实现了 InitializingBean 接口,在 Sping 初始化该 Bean 的时候,会调用该方法,完成一些初始化工作,方法如下:

    @Override
    public void afterPropertiesSet() {
        // 构建 RequestMappingInfo.BuilderConfiguration 对象
        this.config = new RequestMappingInfo.BuilderConfiguration();
        this.config.setUrlPathHelper(getUrlPathHelper());
        this.config.setPathMatcher(getPathMatcher());
        this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
        this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
        this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
        this.config.setContentNegotiationManager(getContentNegotiationManager());
    
        // 调用父类,初始化
        super.afterPropertiesSet();
    }

isHandler

是否还记得 AbstractHandlerMethodMapping 的这个 抽象 方法?在它的 processCandidateBean 方法中,扫描 Spring 中所有 Bean 时会调用,判断是否需要扫描这个 Bean 中的方法,方法如下:

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

@Controller 或者 @RequestMapping 的注解的类才需要进行扫描,是不是很 熟悉

getMappingForMethod

是否还记得 AbstractHandlerMethodMapping 的这个 抽象 方法?在它的 detectHandlerMethods 方法中,用于获取 Method 方法对应的 Mapping 对象,方法如下:

    @Override
    @Nullable
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        // <1> 基于方法上的 @RequestMapping 注解,创建 RequestMappingInfo 对象
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            // <2> 基于类上的 @RequestMapping 注解,合并进去
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
            // <3> 如果有前缀,则设置到 info 中
            String prefix = getPathPrefix(handlerType);
            if (prefix != null) {
                info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
            }
        }
        return info;
    }
  1. 调用 createRequestMappingInfo(AnnotatedElement element) 方法,基于方法上的 @RequestMapping 注解,创建 RequestMappingInfo 对象
        @Nullable
        private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
            // <1> 获得 @RequestMapping 注解
            RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
            // <2> 获得自定义的条件。目前都是空方法,可以无视
            RequestCondition<?> condition = (element instanceof Class ? 
                                             getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
            // <3> 基于 @RequestMapping 注解,创建 RequestMappingInfo 对象
            return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
        }
        
        protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
            // 创建 RequestMappingInfo.Builder 对象,设置对应属性
            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);
            }
            // 创建 RequestMappingInfo 对象
            return builder.options(this.config).build();
        }
  1. 基于类上的 @RequestMapping 注解,合并进去
  2. 如果有前缀,则设置到 info

match

match(HttpServletRequest request, String pattern) 方法,执行匹配,代码如下:

    @Override
    public RequestMatchResult match(HttpServletRequest request, String pattern) {
        // <1> 为 `pattern` 创建一个 RequestMappingInfo 对象
        RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build();
        // <2> 获得请求对应的 RequestMappingInfo 对象
        RequestMappingInfo matchingInfo = info.getMatchingCondition(request);
        if (matchingInfo == null) { // <3> 没有匹配的 RequestMappingInfo 对象返回空
            return null;
        }
        // <4> 获得请求匹配到的路径
        Set<String> patterns = matchingInfo.getPatternsCondition().getPatterns();
        // <5> 获取请求路径
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        // <6> 创建 RequestMatchResult 结果
        return new RequestMatchResult(patterns.iterator().next(), lookupPath, getPathMatcher());
    }

总结

在 Spring MVC 处理请求的过程中,需要通过 HandlerMapping 组件会为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors),该组件体系结构如下:

202303142234398624.png

本文就 红色框 中的内容进行了分析,基于 Method 进行匹配。例如,我们所熟知的 @RequestMapping 等注解的方式

在将 红色框 中的类注入到 Spring 上下文时,会进行一些初始化工作,扫描 @Controller 或者 @RequestMapping 注解标注的 Bean 对象,会将带有 @RequestMapping 注解(包括其子注解)解析成 RequestMappingInfo 对象。接下来,会将 RequestMappingInfo该方法对象该方法所在类对象MappingRegistry 注册表进行注册,其中会生成 HandlerMethod 处理器(方法的所有信息)对象保存起来。当处理某个请求时,HandlerMapping 找到该请求对应的 HandlerMethod 处理器对象后,就可以通过反射调用相应的方法了

这部分内容包含了我们常用到 @Controller@RequestMapping 注解,算是 HandlerMapping 组件的核心内容,看完之后有种 茅塞顿开 的感觉

回到以前的 Servlet 时代,我们需要编写许多的 Servlet 去处理请求,然后在 web.xml 中进行配置,而 Spring MVC 让你通过只要在类和方法上面添加 @Controller 或者 @RequestMapping 注解这种方式,就可以处理请求,因为所有的请求都交给了 DispatcherServlet 去处理。这样是不是简化了你的工作量,让你专注于业务开发。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文