Java教程

地摊秘笈之Spring MVC源码(五)解密HandlerMapping组件

本文主要是介绍地摊秘笈之Spring MVC源码(五)解密HandlerMapping组件,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

HandlerMapping 一系列组件

概述

HandlerMapping一系列组件主要功能是获取处理器执行链HandlerExecutionChain,也就是获取处理器(controller)和拦截器HandlerInterceptor。

HandlerMapping一系列组件里面主要的思路是

  1. 一部分是匹配到最合适的Controller和具体方法,也就是匹配处理器(controller)
  2. 一部分将匹配合适的拦截器HandlerInterceptor

匹配处理器的逻辑:

  1. 首先先将全部Controller里面的每一个添加了@RequestMapping注解的方法收集在一个注册表,注册表里面有很多个map,比如以URL为ke y,以Controller实体和具体方法为value的map。
  2. 那么后面就可以用请求的信息根据注册表中的map进行匹配最合适的Controller和具体方法。

HandlerMapping一系列组件的UML

HandlerMapping一系列组件的主要UML

我们以现在常用的使用注解@RequestMapping的涉及的实现类进行解释

  1. HandlerMapping :该接口也表明组件的关键功能就是用来匹配最合适的处理器(Controller)和拦截器HandlerInterceptor

  2. AbstractHandlerMapping :该抽象类主要提供如果没有获取到合适的处理器则赋值一个默认的处理器,和将合适的拦截器进行收集

  3. AbstractHandlerMethodMapping : 该抽象类主要将全部的Controller里面的是处理器的方法进行收集到注册表中,做了几个map,用于请求的信息去匹配合适的处理器(Controller)

  4. RequestMappingInfoHandlerMapping :该抽象类主要复写handleMatch handleNoMatch`方法,将匹配或者没有匹配的不同情况进行一些具体处理

  5. RequestMappingHandlerMapping : 该实体类主要用来判断是否是标明@Controller@RequestMapping的处理器,和将@RequestMapping里面的信息构建在RequestMappingInfo存储,最后就是初始化一些匹配处理器(Controller)需要的一些辅助类

HandlerMapping

HandlerMapping接口主要作用就是用来获取处理器执行链HandlerExecutionChain,HandlerExecutionChain里面的属性也就是处理器handler(controller)和拦截器(HandlerInterceptor)。

//org.springframework.web.servlet.HandlerMapping
HandlerExecutionChain  getHandler(HttpServletRequest request) throws Except
复制代码

HandlerExecutionChain

HandlerExecutionChain 主要用来存储处理器(controller)和拦截器,查看属性即可明白,同时用来执行拦截器的方法。首先是获取拦截器,然后一些拦截器的方法的执行。

  • 属性

    //org.springframework.web.servlet.HandlerExecutionChain
      //处理器
    	private final Object handler;
    
      //初始化HandlerExecutionChain存储拦截器的对象
    	@Nullable
    	private HandlerInterceptor[] interceptors;
    
      //将拦截器数组interceptors转换成list
    	@Nullable
    	private List<HandlerInterceptor> interceptorList;
    
    	private int interceptorIndex = -1;
    复制代码
  • 拦截器的获取

    • 构造器初始化添加拦截器

      //org.springframework.web.servlet.HandlerExecutionChain
      /**
      	 * 初始化拦截器
      	 * @param handler
      	 * @param interceptors
      	 */
      	public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
      		if (handler instanceof HandlerExecutionChain) {
      			HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
      			this.handler = originalChain.getHandler();
      			this.interceptorList = new ArrayList<>();
      			CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
      			CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
      		}
      		else {
      			this.handler = handler;
      			this.interceptors = interceptors;
      		}
      	}
      复制代码
    • add方法添加拦截器

      //org.springframework.web.servlet.HandlerExecutionChain
      //add方法添加拦截器都先init InterceptorList
      	public void addInterceptor(HandlerInterceptor interceptor) {
      		initInterceptorList().add(interceptor);
      	}
      
      	public void addInterceptor(int index, HandlerInterceptor interceptor) {
      		initInterceptorList().add(index, interceptor);
      	}
      
      	public void addInterceptors(HandlerInterceptor... interceptors) {
      		if (!ObjectUtils.isEmpty(interceptors)) {
      			CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
      		}
      	}
      复制代码
      //org.springframework.web.servlet.HandlerExecutionChain
      /**
      	 * 如果interceptorList先初始化
      	 * @return
      	 */
      	private List<HandlerInterceptor> initInterceptorList() {
      		if (this.interceptorList == null) {
      			this.interceptorList = new ArrayList<>();
      			if (this.interceptors != null) {
      				// An interceptor array specified through the constructor
      				CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
      			}
      		}
      		this.interceptors = null;
      		return this.interceptorList;
      	}
      复制代码
  • 拦截器的方法

    • applyPreHandle方法

      //org.springframework.web.servlet.HandlerExecutionChain
      boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
      		HandlerInterceptor[] interceptors = getInterceptors();
      		if (!ObjectUtils.isEmpty(interceptors)) {
      			for (int i = 0; i < interceptors.length; i++) {
      				HandlerInterceptor interceptor = interceptors[i];
      				//如果执行preHandle失败,则需要将执行成功的拦截器执行afterCompletion
      				if (!interceptor.preHandle(request, response, this.handler)) {
      					triggerAfterCompletion(request, response, null);
      					return false;
      				}
      				this.interceptorIndex = i;
      			}
      		}
      		return true;
      	}
      复制代码
      //org.springframework.web.servlet.HandlerExecutionChain
      void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
      			throws Exception {
      
      		HandlerInterceptor[] interceptors = getInterceptors();
      		if (!ObjectUtils.isEmpty(interceptors)) {
      			//倒序执行执行成功的拦截器的afterCompletion方法
      			for (int i = this.interceptorIndex; i >= 0; i--) {
      				HandlerInterceptor interceptor = interceptors[i];
      				try {
      					interceptor.afterCompletion(request, response, this.handler, ex);
      				}
      				catch (Throwable ex2) {
      					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
      				}
      			}
      		}
      	}
      复制代码
    • applyPostHandle方法

      //org.springframework.web.servlet.HandlerExecutionChain
      void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
      			throws Exception {
      
      		HandlerInterceptor[] interceptors = getInterceptors();
      		if (!ObjectUtils.isEmpty(interceptors)) {
      			for (int i = interceptors.length - 1; i >= 0; i--) {
      				HandlerInterceptor interceptor = interceptors[i];
      				interceptor.postHandle(request, response, this.handler, mv);
      			}
      		}
      	}
      复制代码

AbstractHandlerMapping

该抽象类主要获取处理器的一个骨架方法,同时提供如果没有获取到合适的处理器则赋值一个默认的处理器,和将合适的拦截器进行收集。而匹配最合适的处理器(Controller)的方法getHandlerInternal 作为一个抽象方法给子类进行实现。

  • getHandler方法获取HandlerExecutionChain,这个就是骨架方法

    • 调用getHandlerInternal获取最合适的处理器(Controller),该方法是抽象方法,由子类进行实现获取

    • 调用 getDefaultHandler获取默认的处理器

    • 调用 getHandlerExecutionChain将匹配成功的拦截器存储在 HandlerExecutionChain

      // org.springframework.web.servlet.handler.AbstractHandlerMapping
      public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
      		//获取handler(controller)
      		Object handler = getHandlerInternal(request);
      		if (handler == null) {
      			// 获取默认handler
      			handler = getDefaultHandler();
      		}
      		if (handler == null) {
      			return null;
      		}
      		// Bean name or resolved handler?
      		if (handler instanceof String) {
      			String handlerName = (String) handler;
      			handler = obtainApplicationContext().getBean(handlerName);
      		}
      
      		// 获取handler(controller)和拦截器(HandlerInterceptor)
      		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
      
      		if (logger.isTraceEnabled()) {
      			logger.trace("Mapped to " + handler);
      		}
      		else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
      			logger.debug("Mapped to " + executionChain.getHandler());
      		}
      
      		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
      			CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
      			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      			config = (config != null ? config.combine(handlerConfig) : handlerConfig);
      			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
      		}
      
      		return executionChain;
      	}
      复制代码
    复制代码
  • 调用getHandlerExecutionChain获取HandlerExecutionChain,遍历全部拦截器,将匹配成功的拦截器添加到HandlerExecutionChain

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
       HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
             (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    
       String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
       for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
          if (interceptor instanceof MappedInterceptor) {
             MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
             if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
               //将拦截器添加到HandlerExecutionChain
                chain.addInterceptor(mappedInterceptor.getInterceptor());
             }
          }
          else {
            //将拦截器添加到HandlerExecutionChain
             chain.addInterceptor(interceptor);
          }
       }
       return chain;
    }
    复制代码
  • 其中遍历的来源从[^] 属性作为入口进行了解

    • 属性:拦截器

      // org.springframework.web.servlet.handler.AbstractHandlerMapping
      /**
       	 * 主要用来通过{@link #initInterceptors()}方法将{@link #interceptors}初始化到{@link #adaptedInterceptors}
       	 * 而初始化{@link #interceptors}则通过{@link #setInterceptors(Object...)} 和{@link #extendInterceptors(List)}
       	 */
       	private final List<Object> interceptors = new ArrayList<>();
       
       	/**
       	 * 拦截器
       	 */
       	private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
      复制代码
    • 属性interceptors的初始化方法

      // org.springframework.web.servlet.handler.AbstractHandlerMapping
      public void setInterceptors(Object... interceptors) {
      		this.interceptors.addAll(Arrays.asList(interceptors));
      	}
      复制代码
      // org.springframework.web.servlet.handler.AbstractHandlerMapping
      protected void extendInterceptors(List<Object> interceptors) {
      	}
      复制代码
    • 属性interceptors的用途

      // org.springframework.web.servlet.handler.AbstractHandlerMapping
      protected void initInterceptors() {
      		if (!this.interceptors.isEmpty()) {
      			for (int i = 0; i < this.interceptors.size(); i++) {
      				Object interceptor = this.interceptors.get(i);
      				if (interceptor == null) {
      					throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
      				}
      				this.adaptedInterceptors.add(adaptInterceptor(interceptor));
      			}
      		}
      	}
      复制代码
      // org.springframework.web.servlet.handler.AbstractHandlerMapping
      protected HandlerInterceptor adaptInterceptor(Object interceptor) {
      		if (interceptor instanceof HandlerInterceptor) {
      			return (HandlerInterceptor) interceptor;
      		}
      		else if (interceptor instanceof WebRequestInterceptor) {
      			return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
      		}
      		else {
      			throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
      		}
      	}
      复制代码

HandlerInterceptor

拦截器接口主要是三个方法,而这三个方法的执行时间在整体执行流程里面可以查看,不赘述

  • preHandle

  • 	/**
      	 * 执行时间在{@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)}执行之前
      	 */
      	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      			throws Exception {
      
      		return true;
      	}
    复制代码
  • postHandle

  • /**
     * 执行时间在{@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)}执行完之后执行
     */
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
          @Nullable ModelAndView modelAndView) throws Exception {
    }
    复制代码
  • afterCompletion

  • /**
     * 执行时间在{@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)}执行之前
     */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
          @Nullable Exception ex) throws Exception {
    }
    复制代码

AbstractHandlerMethodMapping

该组件主要有两个主要功能:

  • 将全部的Controller里面的是处理器的方法进行收集到注册表中,做了几个map,用于请求的信息去匹配合适的处理器(Controller)
  • 匹配合适的处理器(Controller)

收集所有@RequestMapping信息到注册表中

  • AbstractHandlerMethodMapping 实现了InitializingBean 接口,在初始化Servlet WebApplicationContext容器的时候初始化组件进行调用

  • 调用getCandidateBeanNames获取所有的Bean的名字

  • 调用processCandidateBean识别是处理器的Bean,同时搜集信息

//org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
public void afterPropertiesSet() {
		initHandlerMethods();
	}

//org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
protected void initHandlerMethods() {
  //getCandidateBeanNames 获取所有Bean名字
	for (String beanName : getCandidateBeanNames()) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
      //识别是处理器的Bean,同时进行搜集信息
			processCandidateBean(beanName);
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

//org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
//获取所有Bean名字
protected String[] getCandidateBeanNames() {
	return (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));
	}
复制代码
  • 调用AbstractHandlerMethodMapping的processCandidateBean 方法

    • 调用isHandler去除不是处理器的Bean,isHandler是抽象方法,具体实现给子类,用于去除不是处理器的Bean
    • 同时搜集信息
    //org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
    protected void processCandidateBean(String beanName) {
    		Class<?> beanType = null;
    		try {
    			beanType = obtainApplicationContext().getType(beanName);
    		}
    		catch (Throwable ex) {
    			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
    			if (logger.isTraceEnabled()) {
    				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
    			}
    		}
       // 判断是否为处理器
    		if (beanType != null && isHandler(beanType)) {
          //搜集信息
    			detectHandlerMethods(beanName);
    		}
    	}
    复制代码
  • 调用detectHandlerMethods方法进行搜集信息,注意入参其实是String类型的Bean的名字

    • 如果是String类型,则通过Bean的名字获取Bean的Class类型

    • 将Bean的方法和方法映射信息的T (比如@RequestMapping)信息做一个Map,getMappingForMethod是抽象方法,给予子类实现

    • 将这些方法和方法映射信息的T组册到注册表中

      //org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
      protected void detectHandlerMethods(Object handler) {
         //如果是String类型,则通过Bean的名字获取Bean的Class类型
      		Class<?> handlerType = (handler instanceof String ?
      				obtainApplicationContext().getType((String) handler) : handler.getClass());
      
      		if (handlerType != null) {
      			Class<?> userType = ClassUtils.getUserClass(handlerType);
            //将Bean的方法和方法映射信息的T (比如@RequestMapping)信息做一个Map
      			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
      					(MethodIntrospector.MetadataLookup<T>) method -> {
      						try {
      							return getMappingForMethod(method, userType);
      						}
      						catch (Throwable ex) {
      							throw new IllegalStateException("Invalid mapping on handler class [" +
      									userType.getName() + "]: " + method, ex);
      						}
      					});
      			if (logger.isTraceEnabled()) {
      				logger.trace(formatMappings(userType, methods));
      			}
            //将这些方法和方法映射信息的T组册到注册表中
      			methods.forEach((method, mapping) -> {
      				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
      				registerHandlerMethod(handler, invocableMethod, mapping);
      			});
      		}
      	}
      复制代码
    • 调用registerHandlerMethod方法进行将方法和方法映射信息T注册到注册表中

      //org.springframework.web.servlet.handler.AbstractHandlerMethodMappin	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
      		this.mappingRegistry.register(mapping, handler, method);
      }
      复制代码
    • 调用AbstractHandlerMethodMapping里面的内部类MappingRegistry的 register方法将方法和方法映射信息T做一些map存储

      • 构造HandlerMethod,也就是将处理器(Controller)和方法存储在一个对象,方便处理

      • 验证是否一个映射信息只有一个方法对应

      • 将映射信息T和方法做一个映射存储,框架可以根据映射信息和请求匹配合适的方法

      • 获取所有没有特殊字符的存粹URL(比如没有/{userId}),将存粹URL和映射信息作一个LinkedHashMap,可以用于框架快速匹配

      • 将命名信息和handlerMethod存储在ConcurrentHashMap

          //org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry
          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 {
              //构造HandlerMethod,也就是将处理器(Controller)和方法存储在一个对象,方便处理
              HandlerMethod handlerMethod = createHandlerMethod(handler, method);
              //验证是否一个映射信息只有一个方法对应
              validateMethodMapping(handlerMethod, mapping);
              //将映射信息T和方法做一个映射存储
              this.mappingLookup.put(mapping, handlerMethod);
        
              //获取所有没有特殊字符的存粹URL(比如没有/{userId})
              List<String> directUrls = getDirectUrls(mapping);
              for (String url : directUrls) {
                //将存粹URL和映射信息作一个LinkedHashMap
                this.urlLookup.add(url, mapping);
              }
        
              String name = null;
              if (getNamingStrategy() != null) {
                //通过命名策略对方法和映射信息进行命名
                name = getNamingStrategy().getName(handlerMethod, mapping);
                //将命名信息和handlerMethod存储在ConcurrentHashMap
                addMappingName(name, handlerMethod);
              }
        
              CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
              if (corsConfig != null) {
                this.corsLookup.put(handlerMethod, corsConfig);
              }
        
              this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            }
            finally {
              this.readWriteLock.writeLock().unlock();
            }
          }
        复制代码
    • 调用createHandlerMethod方法,将映射信息和方法存储在对象HandlerMethod

      //org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
      protected HandlerMethod createHandlerMethod(Object handler, Method method) {
      		if (handler instanceof String) {
      			return new HandlerMethod((String) handler,
      					obtainApplicationContext().getAutowireCapableBeanFactory(), method);
      		}
      		return new HandlerMethod(handler, method);
      	}
      复制代码
    • 调用validateMethodMapping方法,验证一个映射信息是否只有一个方法对应

      //org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
      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.");
      			}
      		}
      复制代码
    • 在AbstractHandlerMapping获取HandlerExecutionChain调用getHandlerInternal获取处理器的复写

      • 获取请求路径,存储在请求request作为缓存,给后面的查找请求路径快速定位

      • 获取最合适的处理器和具体方法对象HandlerMethod

        //org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
        protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
          //获取请求路径
        		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
          //存储在请求request作为缓存,给后面的查找请求路径快速定位
        		request.setAttribute(LOOKUP_PATH, lookupPath);
        		this.mappingRegistry.acquireReadLock();
        		try {
              //获取最合适的处理器和具体方法对象HandlerMethod
        			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        		}
        		finally {
        			this.mappingRegistry.releaseReadLock();
        		}
        	}
        复制代码
    • 调用lookupHandlerMethod获取最合适的处理器和具体方法对象HandlerMethod

      • 存储匹配成功的映射信息和HandlerMethod,用于后面比较最合适的Match

      • 从注册表中存粹路径和映射信息的map中获取

      • 遍历映射信息,尝试获取匹配的映射信息,将信息存储在matches里面

      • 如果还是没有找到则将所有的映射信息遍历,尝试获取匹配的映射信息,将信息存储在matches里面

      • 如果匹配到超过1个Match,则需要比较出最合适的Match,通过比较映射信息匹配最合适的映射信息

      • 如果比较出有两个合适的映射信息则报错,无法定位该使用哪个方法

      • 处理映射信息

      • 如果没有匹配成功则给出报错信息

        //org.springframework.web.servlet.handler.AbstractHandlerMethodMap
        protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
            // 存储匹配成功的映射信息和HandlerMethod,用于后面比较最合适的Match
        		List<Match> matches = new ArrayList<>();
            //从注册表中存粹路径和映射信息的map中获取
        		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        		if (directPathMatches != null) {
              //遍历映射信息,尝试获取匹配的映射信息,将信息存储在matches里面
        			addMatchingMappings(directPathMatches, matches, request);
        		}
        		if (matches.isEmpty()) {
        			// No choice but to go through all mappings...
        // 如果还是没有找到则将所有的映射信息遍历,尝试获取匹配的映射信息,将信息存储在matches里面
              addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        		}
        
        		if (!matches.isEmpty()) {
        			Match bestMatch = matches.get(0);
        			if (matches.size() > 1) {
                // 如果匹配到超过1个Match,则需要比较出最合适的Match,通过比较映射信息匹配最合适的映射信息
        				Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        				matches.sort(comparator);
        				bestMatch = matches.get(0);
        				if (logger.isTraceEnabled()) {
        					logger.trace(matches.size() + " matching mappings: " + matches);
        				}
        				if (CorsUtils.isPreFlightRequest(request)) {
        					return PREFLIGHT_AMBIGUOUS_MATCH;
        				}
        				Match secondBestMatch = matches.get(1);
                // 如果比较出有两个合适的映射信息则报错,无法定位该使用哪个方法
        				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
        					Method m1 = bestMatch.handlerMethod.getMethod();
        					Method m2 = secondBestMatch.handlerMethod.getMethod();
        					String uri = request.getRequestURI();
        					throw new IllegalStateException(
        							"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
        				}
        			}
        			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
              //处理映射信息
        			handleMatch(bestMatch.mapping, lookupPath, request);
        			return bestMatch.handlerMethod;
        		}
        		else {
              // 如果没有匹配成功则给出报错信息
        			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        		}
        	}
        复制代码
    • 调用addMatchingMappings方法,尝试获取匹配的映射信息存储在Match

      • 调用getMatchingMapping尝试获取匹配的映射信息,如果匹配的话会生成新的映射信息,其中 getMatchingMapping是抽象方法,给予子类实现
      • 将匹配的映射信息和HandlerMethod存储在Match
      //org.springframework.web.servlet.handler.AbstractHandlerMethodMap
      private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
      		for (T mapping : mappings) {
            //尝试获取匹配的映射信息,如果匹配的话会生成新的映射信息
      			T match = getMatchingMapping(mapping, request);
      			if (match != null) {
              //将匹配的映射信息和HandlerMethod存储在Match
      				matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
      			}
      		}
      	}
      复制代码
    • 调用比较器进行排序,将最合适的放第一个,而比较的是映射信息

      	//org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MatchComparator
      	private class MatchComparator implements Comparator<Match> {
      
      		private final Comparator<T> comparator;
      
      		public MatchComparator(Comparator<T> comparator) {
      			this.comparator = comparator;
      		}
      
      		@Override
      		public int compare(Match match1, Match match2) {
            //使用映射信息进行比较
      			return this.comparator.compare(match1.mapping, match2.mapping);
      		}
      	}
      复制代码
    • 调用handleMatch方法处理匹配信息,该方法会被子类复写,将更多的信息存储在request里面

    //org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
    protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
    		request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
    	}
    复制代码
    • 调用handleNoMatch处理没有匹配方法情况,该方法被子类复写进行一些错误信息的处理

      //org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
      	protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
      			throws Exception {
      
      		return null;
      	}
      复制代码

RequestMappingInfoHandlerMapping

RequestMappingInfoHandlerMapping 组件主要复写AbstractHandlerMethodMapping的getMatchingMapping尝试获取映射信息和比较器getMappingComparatorhandleMatch处理匹配信息和handleNoMatch处理没有匹配映射信息

  • getMatchingMapping用于尝试获取映射信息,该方法主要是映射信息的方法,在最后一个实现类进行解释

    //org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping
    	protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
    		return info.getMatchingCondition(request);
    	}
    复制代码
  • getMappingComparator用于比较多个匹配成功的信息进行排序,该方法主要是映射信息的方法,在最后一个实现类进行解释

    //org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping
    	protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
    		return (info1, info2) -> info1.compareTo(info2, request);
    	}
    复制代码
  • handleMatch 方法将一些属性进行存储

    //org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping
    protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
    		super.handleMatch(info, lookupPath, request);
    
    		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);
    
    		if (isMatrixVariableContentAvailable()) {
    			Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
    			request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
    		}
    
    		Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
    		request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
    
    		if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
    			Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
    			request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
    		}
    	}
    复制代码
  • handleNoMatch方法处理不匹配的情况说明

  • HttpMethod方法不匹配

  • Content-Type 不匹配

  • 参数不匹配

    //org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping
    protected HandlerMethod handleNoMatch(
    			Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {
    
    		PartialMatchHelper helper = new PartialMatchHelper(infos, request);
    		if (helper.isEmpty()) {
    			return null;
    		}
    
      //HttpMethod方法不匹配
    		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);
    		}
    
      //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));
    		}
        
    		if (helper.hasProducesMismatch()) {
    			Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
    			throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
    		}
    
      //参数不匹配
    		if (helper.hasParamsMismatch()) {
    			List<String[]> conditions = helper.getParamConditions();
    			throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
    		}
    
    		return null;
    	}
    复制代码

RequestMappingInfo

该类就是我们一直在前面说的映射信息T,它的核心的功能有

  1. 存储写在方法或者类上的@RequestMapping的信息
  2. combine用于和另外一个映射信息进行合并
  3. getMatchingCondition匹配合适的映射信息
  4. compareTo比较两个映射信息,用来排序

下面分别解释

  • 用属性存储写在方法或者类上的@RequestMapping的信息

    //org.springframework.web.servlet.mvc.method.RequestMappingInfo	
      @Nullable
    	private final String name;
      
      //路径匹配条件
    	private final PatternsRequestCondition patternsCondition;
      //Method方法匹配条件
    	private final RequestMethodsRequestCondition methodsCondition;
      //参数匹配条件
    	private final ParamsRequestCondition paramsCondition;
      //请求头匹配条件
    	private final HeadersRequestCondition headersCondition;
      
    	private final ConsumesRequestCondition consumesCondition;
    	
    	private final ProducesRequestCondition producesCondition;
      //自定义的条件
    	private final RequestConditionHolder customConditionHolder;
    复制代码
  • combine将两个映射信息进行合并

    //org.springframework.web.servlet.mvc.method.RequestMappingInfo		
    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());
    	}
    复制代码
  • getMatchingCondition根据请求尝试获取匹配的映射信息,将映射信息里面的每一个条件进行匹配,如果其中一个方法没有匹配成功则返回null,如果有匹配成功的则最终将每一个新的条件作为属性生成新的映射信息

    //org.springframework.web.servlet.mvc.method.RequestMappingInfo	
    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());
    	}
    复制代码
  • compareTo比较两个映射信息,比较映射信息的每一个条件如果不相等则继续往下一个条件进行比较

//org.springframework.web.servlet.mvc.method.RequestMappingInfo	
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
		int result;
		// Automatic vs explicit HTTP HEAD mapping
		if (HttpMethod.HEAD.matches(request.getMethod())) {
			result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
			if (result != 0) {
				return result;
			}
		}
		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;
	}
复制代码

RequestMappingHandlerMapping

RequestMappingHandlerMapping 实现类主要复写了AbstractHandlerMethodMapping的isHandler用于判断Bean是否是处理器和复写AbstractHandlerMethodMapping的getMappingForMethod用于构造映射信息

  • 复写AbstractHandlerMethodMapping的isHandler判断Bean是否是处理器,也就是判断是否有注解@Controller@RequestMapping标注

    //org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping	
    protected boolean isHandler(Class<?> beanType) {
    		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
    				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    	}
    
    复制代码
  • 复写AbstractHandlerMethodMapping的getMappingForMethod用于构造映射信息

    • 调用createRequestMappingInfo通过方法进行构造映射信息RequestMappingInfo

    • 调用createRequestMappingInfo通过处理器类型进行构造映射信息

      //org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
      protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        //通过方法进行构造映射信息RequestMappingInfo
      		RequestMappingInfo info = createRequestMappingInfo(method);
      		if (info != null) {
            //通过处理器类型进行构造映射信息
      			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      			if (typeInfo != null) {
      				info = typeInfo.combine(info);
      			}
      			String prefix = getPathPrefix(handlerType);
      			if (prefix != null) {
      				info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
      			}
      		}
      		return info;
      	}
      复制代码
  • 调用createRequestMappingInfo构造映射信息

    //org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping	
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    		RequestCondition<?> condition = (element instanceof Class ?
    				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
      //构造映射信息
    		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    	}
    复制代码
  • 调用createRequestMappingInfo构造映射信息

    • 将方法或者类里面的注解@RequestMapping里面的信息使用RequestMappingInfo.Builder进行构造,方法简单不赘述
    //org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
    protected RequestMappingInfo createRequestMappingInfo(
    			RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
    
    		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();
    	}
    复制代码

HandlerMapping组件的初始化

HandlerMapping组件的初始化我们在初始化Servlet WebApplicationContext里面没有细说,这次补上,主要分三种情况

  1. 通过Servlet WebApplicationContext容器查找HandlerMapping类型的Bean
  2. 通过Servlet WebApplicationContext容器查找名字为handlerMapping类型为HandlerMapping的Bean
  3. 从默认配置获取Class的全类名,通过反射初始化

Servlet WebApplicationContext容器初始化最后调用onRefresh方法,再调用initStrategies方法初始化全部组件,其中调用initHandlerMappings初始化处理器映射器组件,从initHandlerMappings这个方法开始解释

  • initHandlerMappings方法,分三种情况,不赘述

    //org.springframework.web.servlet.DispatcherServlet
    private void initHandlerMappings(ApplicationContext context) {
    		this.handlerMappings = null;
    
    		//如果开启检测全部HandlerMapping的Bean则从已经注入的HandlerMapping的Bean进行初始化
    		if (this.detectAllHandlerMappings) {
    			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
    			Map<String, HandlerMapping> matchingBeans =
    					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
    			if (!matchingBeans.isEmpty()) {
    				this.handlerMappings = new ArrayList<>(matchingBeans.values());
    				// We keep HandlerMappings in sorted order.
    				AnnotationAwareOrderComparator.sort(this.handlerMappings);
    			}
    		}
    		else {
    			try {
    				//如果不检测全部HandlerMapping的Bean,则根据HANDLER_MAPPING_BEAN_NAME获取
    				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
    				this.handlerMappings = Collections.singletonList(hm);
    			}
    			catch (NoSuchBeanDefinitionException ex) {
    				// Ignore, we'll add a default HandlerMapping later.
    			}
    		}
    
    		// Ensure we have at least one HandlerMapping, by registering
    		// a default HandlerMapping if no other mappings are found.
    		//从配置文件获取默认的HandlerMapping全类名进行构造Bean初始化组件
    		if (this.handlerMappings == null) {
    			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    			if (logger.isTraceEnabled()) {
    				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
    						"': using default strategies from DispatcherServlet.properties");
    			}
    		}
    	}
    复制代码
  • 调用getDefaultStrategies方法

    • 从默认配置获取处理器映射器的全类名,全类名有

      • org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
      • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
      • org.springframework.web.servlet.function.support.RouterFunctionMapping
    • 通过反射获取Class类

    • 根据Class类通过Servlet WebApplicationContext容器构建Bean进行初始化

      //org.springframework.web.servlet.DispatcherServlet
      protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
      		String key = strategyInterface.getName();
      		//DispatcherServlet.properties获取默认配置为
      		// org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
      		//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
      		String value = defaultStrategies.getProperty(key);
      		if (value != null) {
      			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
      			List<T> strategies = new ArrayList<>(classNames.length);
      			for (String className : classNames) {
      				try {
                //通过反射获取Class类
      					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                //根据Class类通过Servlet WebApplicationContext容器构建Bean进行初始化
      					Object strategy = createDefaultStrategy(context, clazz);
      					strategies.add((T) strategy);
      				}
      				catch (ClassNotFoundException ex) {
      					throw new BeanInitializationException(
      							"Could not find DispatcherServlet's default strategy class [" + className +
      							"] for interface [" + key + "]", ex);
      				}
      				catch (LinkageError err) {
      					throw new BeanInitializationException(
      							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
      							className + "] for interface [" + key + "]", err);
      				}
      			}
      			return strategies;
      		}
      		else {
      			return new LinkedList<>();
      		}
      	}
      复制代码
  • defaultStrategies属性的初始化

    • 文件的位置DEFAULT_STRATEGIES_PATH = DispatcherServlet.properties

      //org.springframework.web.servlet.DispatcherServlet	
      private static final Properties defaultStrategies;
      
      	static {
      		// Load default strategy implementations from properties file.
      		// This is currently strictly internal and not meant to be customized
      		// by application developers.
      		try {
            //文件的位置DEFAULT_STRATEGIES_PATH = DispatcherServlet.properties
      			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
      			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
      		}
      		catch (IOException ex) {
      			throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
      		}
      	}
      复制代码
  • 调用createDefaultStrategy通过类型进行构建Bean初始化处理器映射器组件

    protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
    		return context.getAutowireCapableBeanFactory().createBean(clazz);
    	}
    复制代码
这篇关于地摊秘笈之Spring MVC源码(五)解密HandlerMapping组件的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!