SpringMVC源码解读 --- 处理器映射器(HandlerMapping) 各种处理器映射器的使用及其源码分析

本文深入剖析SpringMVC框架中HandlerMapping接口及其核心实现,包括AbstractHandlerMapping、AbstractUrlHandlerMapping、AbstractHandlerMethodMapping等类的工作原理。详细解读了处理器映射器如何根据请求选择合适的处理器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  这一篇我们来梳理下SpringMVC中关于HandlerMapping接口及其实现类。

      首先简单来看下DispatcherServlet对于请求的简单执行流程,了解下大概(关于这个过程的具体细节到时候在看还需不需要来一下具体细节),实现是根据request获取HandlerExecutionChain(其包含根据映射获取到的对应方法或者类handler(Object)通过对应的HandlerMapping获取)、HandlerInterceptor),然后通过HandlerAdapter 的supports判断当前的handler是那种,再获取对应的HandlerAdapter ,然后执行handler获取ModelAndView,再通过processDispatchResult方法处理(有些会要用到ViewResolver去处理) 。   

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;
       ..........
   try {
      ModelAndView mv = null;
      Exception dispatchException = null; 
         // Determine handler for the current request.
         mappedHandler = getHandler(processedRequest);
         .........
         // Determine handler adapter for the current request.
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
           ..........
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
         // Actually invoke the handler.
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
         ............
         applyDefaultViewName(processedRequest, mv);
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
        ..............
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
    ..............
}

  处理器映射器(HandlerMapping)

         1、首先我们来看下这个接口本身

public interface HandlerMapping {

   String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

   String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

   String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";

   String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

   String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";

   String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

   @Nullable
   HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

        其除了一些类变量,只有一个方法getHandler,然后其入参是HttpServletReqeust,返回是HandlerExecutionChain,所以这个接口就是根据请求的request返回一个处理执行链,有点类似于Tomcat中ApplicationFilterChain的。

       我们现在来看下这个HandlerExecutionChain:

public class HandlerExecutionChain {
   private final Object handler;
   @Nullable
   private HandlerInterceptor[] interceptors;
   @Nullable
   private List<HandlerInterceptor> interceptorList;
   private int interceptorIndex = -1;
     ..................

   boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
       ..................
   }

   void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
         throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
        ..............
   }
        ................
   void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
      HandlerInterceptor[] interceptors = getInterceptors();
   .............
   }
}

  可以看到这个类主要有两种成员变量handler、HandlerInterceptor,这两个一个是我们获取要处理的方法或者类(这个到后面就能明白这个变量)、还有一个就是拦截器,这个就是我们再请求一个参数的时候首先需要判断拦截器,看能不能让其最终请求到对应的处理,这个也就是类型我们再前面梳理tomcat的时候,首先是经过所有的Filter,通过后才会运行最终Servlet的service方法。

       2、现在我们来看下HandlerMapping的实现类

       首先是其的抽象实现类:AbstractHandlerMapping     

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
   @Nullable
   private Object defaultHandler;
   private UrlPathHelper urlPathHelper = new UrlPathHelper();
   private PathMatcher pathMatcher = new AntPathMatcher();
   private final List<Object> interceptors = new ArrayList<>();
   private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
   private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource();
     ...............

   @Override
   @Nullable
   public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
      Object handler = getHandlerInternal(request);
      if (handler == null) {
         handler = getDefaultHandler();
      }
      if (handler == null) {
         return null;
      }
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
      if (CorsUtils.isCorsRequest(request)) {
         CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
         CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
         CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
         executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
      }
      return executionChain;
   }

   @Nullable
   protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

   protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
      HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

      String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
      for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
         if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
               chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
         }
         else {
            chain.addInterceptor(interceptor);
         }
      }
      return chain;
   }
     ............
}

可以看到前面获取的HandlerExecutionChain就是这个抽象类的getHandler返回的。然后getHandler方法调用的getHandlerInternal是一个抽象方法,需要其子类实现。

    然后是AbstractHandlerMapping的两个主要子类:AbstractUrlHandlerMapping、AbstractHandlerMethodMapping,这两个Abstract类走的是两种路线。

 

    HandlerMapping是怎样使用的

       首先我们看下AbstractUrlHandlerMapping的实现子类:

       1、SimpleUrlHandlerMapping:

              我们通过Spring本身的测试demo来看下这个子类的具体使用: 

    map2.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
   <bean id="urlMapping1" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!--      <property name="defaultHandler"><ref bean="starController"/></property>-->
<!--      <property name="rootHandler"><ref bean="mainController"/></property>-->
      <property name="urlMap">
         <map>
            <entry key="welcome.html"><ref bean="mainController"/></entry>
            <entry key="oth*.html"><ref bean="otherController"/></entry>
         </map>
      </property>
   </bean>

   <bean id="urlMapping2" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!--      <property name="defaultHandler"><ref bean="starController"/></property>-->
<!--      <property name="rootHandler"><ref bean="mainController"/></property>-->
      <property name="mappings"><ref bean="propsForUrlMapping2"/></property>
   </bean>
   <bean id="propsForUrlMapping2" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
      <property name="location"><value>map2.properties</value></property>
   </bean>

    <bean id="urlMapping3" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="urlDecode"><value>true</value></property>
        <property name="mappings">
            <value>
                welcome.html=mainController
                star.html=mainController
            </value>
        </property>
    </bean>
    <bean id="mainController" class="java.lang.Object"/>
    <bean id="starController" class="java.lang.Object"/>
    <bean id="otherController" class="java.lang.Object"/>
</beans>   

map2.properties文件的内容:

welcome.html= mainController
oth*.html = otherController

     其对应的测试demo

public class SpringSimpleUrlHandlerMappingTest
{
    private XmlWebApplicationContext wac;
    @Before
    public void before()
    {
        MockServletContext sc = new MockServletContext("");
        wac = new XmlWebApplicationContext();
        wac.setServletContext(sc);
        wac.setConfigLocations(new String[] {"map2.xml"});
        wac.refresh();

    }
    @Test
    public void urlMapping1()
        throws Exception
    {
        HandlerMapping hm = (HandlerMapping) wac.getBean("urlMapping1");
        MockHttpServletRequest welcomeRequest = new MockHttpServletRequest("GET", "/welcome.html");
        MockHttpServletRequest otherRequest = new MockHttpServletRequest("GET", "/other.html");
        HandlerExecutionChain welcomeChain = getHandler(hm, welcomeRequest);
        HandlerExecutionChain otherChain = getHandler(hm, otherRequest);
        System.out.println("");
    }
    @Test
    public void urlMapping2()
        throws Exception
    {
        HandlerMapping hm = (HandlerMapping) wac.getBean("urlMapping2");
         MockHttpServletRequest welcomeRequest = new MockHttpServletRequest("GET", "/welcome.html");
         MockHttpServletRequest otherRequest = new MockHttpServletRequest("GET", "/other.html");
        HandlerExecutionChain welcomeChain = getHandler(hm, welcomeRequest);
        HandlerExecutionChain otherChain = getHandler(hm, otherRequest);
        System.out.println("");
    }

    @Test
    public void urlMapping3()
         throws Exception
    {
        HandlerMapping hm = (HandlerMapping) wac.getBean("urlMapping3");
        MockHttpServletRequest welcomeRequest = new MockHttpServletRequest("GET", "/welcome.html");
        MockHttpServletRequest otherRequest = new MockHttpServletRequest("GET", "/star.html");
        HandlerExecutionChain welcomeChain = getHandler(hm, welcomeRequest);
        HandlerExecutionChain otherChain = getHandler(hm, otherRequest);
        System.out.println("");
    }

    private static HandlerExecutionChain getHandler(HandlerMapping hm, MockHttpServletRequest req) throws Exception {
    HandlerExecutionChain hec = hm.getHandler(req);
    HandlerInterceptor[] interceptors = hec.getInterceptors();
    if (interceptors != null) {
        for (HandlerInterceptor interceptor : interceptors) {
            interceptor.preHandle(req, null, hec.getHandler());
        }
    }
    return hec;
}

         这里的MockHttpServletRequest我们可以看做一个request类似(最开始mappedHandler = getHandler(processedRequest)的processedRequest),你也可以将前面map.xml的内容写到tomcat项目的spring配置文件中这样去测试。然后上面的getHandler方法类似于DispatcherServlet的源码:

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping hm : this.handlerMappings) {
         HandlerExecutionChain handler = hm.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}

可以看到DispatcherServlet的源码是需要遍历HandlerMapping,而这里demo测试是直接传入的HandlerMapping。

       而map.xml中 <bean id="mainController" class="java.lang.Object"/> 这个class = Object也只是一个代表。你如果正常使用需要使用例如基础Controller接口、HttpRequestHandler、Servlet接口等的实现类(这个就是前面讲的handler,具体为什么是这些,到源码梳理的时候再进行说明):

public class RequestController implements Controller
{
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
        throws Exception
    {
        return new ModelAndView();
    }
}
public class HttpRequestHandlerController implements HttpRequestHandler
{
    @Override
    public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
        throws ServletException, IOException
    {
        System.out.println("");
    }
}

这里我们通过urlMapping1、urlMapping2、urlMapping3,分别介绍了BeanNameUrlHandlerMapping三种用法。

   2、BeanNameUrlHandlerMapping:(同样我们来看下其在spring源码测试的使用demo)

      map1.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
    <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <bean id="godCtrl" name="/ /mypath/welcome.html /mypath/show.html /mypath/bookseats.html /mypath/reservation.html /mypath/payment.html /mypath/confirmation.html /mypath/test*"
     class="java.lang.Object"/>
</beans>
public class SpringBeanNameUrlHandlerMappingTest
{
    private ConfigurableWebApplicationContext wac;
    @Before
    public void setUp() throws Exception {
        MockServletContext sc = new MockServletContext("");
        wac = new XmlWebApplicationContext();
        wac.setServletContext(sc);
        wac.setConfigLocation("map1.xml");
        wac.refresh();
    }
    @Test
    public void handlerMapping1()
        throws Exception
    {
        HandlerMapping hm = (HandlerMapping) wac.getBean("handlerMapping");
        MockHttpServletRequest req = new MockHttpServletRequest("GET", "/mypath/welcome.html");
        HandlerExecutionChain hec = hm.getHandler(req);
        Object handler = hec.getHandler();
        System.out.println("");
    }
}

     这个就是BeanNameUrlHandlerMapping的用法,可能首先看到map1.xml中的配置比较奇怪,因为并没有将BeanNameUrlHandlerMapping与对应的handler建立联系,但其其是这里的特殊就是,<bean>标签的的name属性有多个name(以空格分开),每个name就是对应当前bean的别名(同时别名也可以用alias 其设置)。这个BeanNameUrlHandlerMapping的作用就是去判断bean标签的name有没有以"/"开头的,如果以"/"开头就将其注册到BeanNameUrlHandlerMapping中,建立映射关系。

  在Spring中关于AbstractUrlHandlerMapping子类的实现就是这两个,下面我们来看下AbstractHandlerMethodMapping子类的使用。

   1、RequestMappingHandlerMapping

      AbstractUrlHandlerMapping其还有一个Abstract子类:RequestMappingInfoHandlerMapping其是一个抽象类(很奇怪,按Spring的命名规则,应该是以Abstract开头的),然后RequestMappingHandlerMapping继承的是RequestMappingInfoHandlerMapping。所以其实AbstractHandlerMethodMapping这个在SpringMVC中去只有一个实现类就是RequestMappingHandlerMapping,这个RequestMappingHandlerMapping的使用就是通过@Controller、@RequestMapping注解注入的映射方法,我们通过AbstractHandlerMethodMapping这个名词,其有Method。

       例如我们再springmvc配置的xml文件中:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    <context:component-scan base-package="web" />
    <mvc:annotation-driven/>
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="jspViewResolver">
    <property value="org.springframework.web.servlet.view.JstlView" name="viewClass"/>
    <property value="/WEB-INF/" name="prefix"/>
    <property value=".jsp" name="suffix"/>
   </bean>
</beans>

再在对应的web包下创建SpringController类:

@Controller
public class SpringController{

    @RequestMapping(value = "/spring", method = RequestMethod.GET)
    public String springMethod() {
        return "springMethod";
    }
}

 再在/WEB-INF/目录下创建名词为springMethod的jsp文件:

 springMethod.jsp文件

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" isELIgnored="false"%>

<h1>Hello World</h1>

   HandlerMapping的源码解析

  我们在上面讲过HandlerMapping这个接口 本身就是一个getHandler(HttpServletRequest request)方法,通过request获取HandlerExecutionChain。

public interface HandlerMapping {
   @Nullable
   HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

   1、首先我们看下其的基本实现AbstractHandlerMapping

         

        通过这个结构关系,我们看到有两个Aware接口:ServletContextAware、ApplicationContextAware。这两个Awre接口就是去获取容器本身ApplicationContext以及ServletContext。然后AbstractHandlerMapping应该能从这两个组件中获取一些信息去处理。

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
   @Nullable
   private Object defaultHandler;
   private UrlPathHelper urlPathHelper = new UrlPathHelper();
   private PathMatcher pathMatcher = new AntPathMatcher();
   private final List<Object> interceptors = new ArrayList<>();

   private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

   private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource();

   private CorsProcessor corsProcessor = new DefaultCorsProcessor();
      .............
   public void setDefaultHandler(@Nullable Object defaultHandler) {
      this.defaultHandler = defaultHandler;
   }
   .............
   @Nullable
   public Object getDefaultHandler() {
      return this.defaultHandler;
   }

   public void setInterceptors(Object... interceptors) {
      this.interceptors.addAll(Arrays.asList(interceptors));
   }
     ..............(cors跨域相关)
   @Override
   protected void initApplicationContext() throws BeansException {
      extendInterceptors(this.interceptors);
      detectMappedInterceptors(this.adaptedInterceptors);
      initInterceptors();
   }

   protected void extendInterceptors(List<Object> interceptors) {
   }

   protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
      mappedInterceptors.addAll(
            BeanFactoryUtils.beansOfTypeIncludingAncestors(
                  obtainApplicationContext(), MappedInterceptor.class, true, false).values());
   }
   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));
         }
      }
   }

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

   /**
    * Return all configured {@link MappedInterceptor}s as an array.
    * @return the array of {@link MappedInterceptor}s, or {@code null} if none
    */
   @Nullable
   protected final MappedInterceptor[] getMappedInterceptors() {
      List<MappedInterceptor> mappedInterceptors = new ArrayList<>();
      for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
         if (interceptor instanceof MappedInterceptor) {
            mappedInterceptors.add((MappedInterceptor) interceptor);
         }
      }
      int count = mappedInterceptors.size();
      return (count > 0 ? mappedInterceptors.toArray(new MappedInterceptor[count]) : null);
   }


   @Override
   @Nullable
   public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
      Object handler = getHandlerInternal(request);
      if (handler == null) {
         handler = getDefaultHandler();
      }
      if (handler == null) {
         return null;
      }
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
     .............(cors跨域处理)
      return executionChain;
   }

   @Nullable
   protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

   protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
      HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

      String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
      for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
         if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
               chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
         }
         else {
            chain.addInterceptor(interceptor);
         }
      }
      return chain;
   }
    ..............
}

    可以看到这里的关键成员变量有defaultHandler(默认的handler)、interceptors(拦截器)(cors跨域先跳过),

    1、然后有getHandler(HttpServletRequest request)方法

        这个方法就是获取handler,然后有abstract方法getHandlerInternal,其子类实现。如果getHandlerInternal方法没有获取到就去获取defaultHandler,同时这里如果可以获取到的话如果handler是String类型,还需要从applicationContext中获取对应的handler Bean。

   2、getHandlerExecutionChain方法:

       这个方法是获取HandlerExecutionChain,首先是将handler设置到HandlerExecutionChain中,再通过urlPathHelper获取请求的对应处理的地址,然后再判断有哪些MappedInterceptor是拦截该handler的,如果是就将该MappedInterceptor添加到HandlerExecutionChain中。

   然后这里就有一个疑问了,这个MappedInterceptor是什么时候设置进来的呢?这个就要回到前面讲的ApplicationContextAware接口,这个接口就是将ApplicationContext设置到当前对象,然后其有initApplicationContext方法

@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
          ............
      initApplicationContext(context);
   }
      ...........
}

     然后调用到AbstractHandlerMapping的initApplicationContext方法

@Override
protected void initApplicationContext() throws BeansException {
   extendInterceptors(this.interceptors);
   detectMappedInterceptors(this.adaptedInterceptors);
   initInterceptors();
}

   extendInterceptors方法是一个空方法,detectMappedInterceptors方法:

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
   mappedInterceptors.addAll(
         BeanFactoryUtils.beansOfTypeIncludingAncestors(
               obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

这个方法就是从容器中获取MappedInterceptor接口的实现类,将其添加到mappedInterceptors中

public interface HandlerInterceptor {

   default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      return true;
   }

   default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable ModelAndView modelAndView) throws Exception {
   }

   default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable Exception ex) throws Exception {
   }
}

      再调用initInterceptors方法将interceptors添加到adaptInterceptor中

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));
      }
   }
}
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());
   }
}
public WebRequestHandlerInterceptorAdapter(WebRequestInterceptor requestInterceptor) {
   Assert.notNull(requestInterceptor, "WebRequestInterceptor must not be null");
   this.requestInterceptor = requestInterceptor;
}

       这里有区分如果是WebRequestInterceptor的Interceptor将其转换到适配器WebRequestHandlerInterceptorAdapter。同时在前面的getHandlerExecutionChain有区分是不是MappedInterceptor:

public final class MappedInterceptor implements HandlerInterceptor {

   @Nullable
   private final String[] includePatterns;
   @Nullable
   private final String[] excludePatterns;
   private final HandlerInterceptor interceptor;
   @Nullable
   private PathMatcher pathMatcher;
     .............
  public boolean matches(String lookupPath, PathMatcher pathMatcher) {
   PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher);
   if (!ObjectUtils.isEmpty(this.excludePatterns)) {
      for (String pattern : this.excludePatterns) {
         if (pathMatcherToUse.match(pattern, lookupPath)) {
            return false;
         }
      }
   }
   if (ObjectUtils.isEmpty(this.includePatterns)) {
      return true;
   }
   for (String pattern : this.includePatterns) {
      if (pathMatcherToUse.match(pattern, lookupPath)) {
         return true;
      }
   }
   return false;
}
   ........
}

    如果是MappedInterceptor(HandlerInterceptor 接口的实现类)要调用matchers判断,这里就能变量名知道其的含义,例如放在includePatterns的就代表要使用interceptor,如果是excludePatterns中的地址就代表不要使用当前interceptor。

   2、AbstractHandlerMapping类的子类

       1、AbstractUrlHandlerMapping

public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {
   @Nullable
   private Object rootHandler;
   private boolean useTrailingSlashMatch = false;
   private boolean lazyInitHandlers = false;
   private final Map<String, Object> handlerMap = new LinkedHashMap<>();
    .............
   @Override
   @Nullable
   protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
      String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        ................
      return handler;
   }
   @Nullable
   protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
      // Direct match?
      Object handler = this.handlerMap.get(urlPath);
      if (handler != null) {
         // Bean name or resolved handler?
         if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
         }
         validateHandler(handler, request);
         return buildPathExposingHandler(handler, urlPath, urlPath, null);
      }
         ...........
      return null;
   }
     ...............
   protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
      Assert.notNull(urlPaths, "URL path array must not be null");
      for (String urlPath : urlPaths) {
         registerHandler(urlPath, beanName);
      }
   }

   protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
      Object resolvedHandler = handler;

        ................
   }
    .................
}

      我们看到关于AbstractUrlHandlerMapping其有两个主要的成员变量:rootHandler、handlerMap。handlerMap就是放对应的请求地址与对应的处理器handler(类似于配置的Servlet请求地址与对应的Servlet)的映射关系。

      那这个是怎样的put的呢,这里就使用了registerHandler方法,这个方法的调用是在实现其的子类中调用的,我们看下这个方法的具体逻辑:

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
   Object resolvedHandler = handler;

   // Eagerly resolve handler if referencing singleton via name.
   if (!this.lazyInitHandlers && handler instanceof String) {
      String handlerName = (String) handler;
      ApplicationContext applicationContext = obtainApplicationContext();
      if (applicationContext.isSingleton(handlerName)) {
         resolvedHandler = applicationContext.getBean(handlerName);
      }
   }
   Object mappedHandler = this.handlerMap.get(urlPath);
   if (mappedHandler != null) {
      if (mappedHandler != resolvedHandler) {
         throw new IllegalStateException(
               "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
               "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
      }
   }
   else {
      if (urlPath.equals("/")) {
         setRootHandler(resolvedHandler);
      }
      else if (urlPath.equals("/*")) {
         setDefaultHandler(resolvedHandler);
      }
      else {
         this.handlerMap.put(urlPath, resolvedHandler);
      }
   }
}

   如果传入的handler是String类型,并且不需要懒加载Handler,就将其从applicationContext中获取处理。再从handlerMap中获取,如果能获取出来,但这两个不相等就报错,如果不能获取出来看urlPath,如果是"/",将将当前Handler赋值到RootHandler,如果是"/*"就赋值到DefaultHandler,都不是就按正常的处理put到handlerMap中。

     我们梳理了handlerMap的put,接下来我们就来梳理这个handlerMap的使用,这里就是AbstractUrlHandlerMapping实现的其父类的抽象方法getHandlerInternal:

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   Object handler = lookupHandler(lookupPath, request);
   if (handler == null) {
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
         // Bean name or resolved handler?
         if (rawHandler instanceof String) {
            String handlerName = (String) rawHandler;
            rawHandler = obtainApplicationContext().getBean(handlerName);
         }
         validateHandler(rawHandler, request);
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
   return handler;
}

    这里是先通过UrlPathHelper获取请求的地址,这个getUrlPathHelper().getLookupPathForRequest(request)方法就不具体来梳理了,简单说一下这里面的处理,首先需要了解一个request中不同方法获取不同的地址,可以看下这篇文章或者这篇。然后这里面还有两个知识点,一个就是你你一个正常的请求例如http://localhost:8080/demo1/spring/hello。但其实你是可以写成这样的: http://localhost:8080//demo1//spring//hello,就是说spring会将你请求的requset中的"//"转换为"/"。第二个就我记得原来看关于HTTP相关的书上,一个http的url中是可以有";",这种用法不是很常见,我也忘了,不过在getLookupPathForRequest方法中是会有对应的处理的。

   获取到请求的lookupPath后(这个就是去掉了表示项目名称后的表示获取servlet的地址,我们知道tomcat有四种容器,Host下面可以有多个Context,所以你正常的一个请求是:域名+Context对应的项目名+对应servlet(相当于springMVC中对应获取handler的地址)的请求地址)。这里的lookupPath就是对应的servlet的请求地址。

      然后根据这个lookupPath去获取对应的handler。如果你请求的是正常的设置,例如我们前面讲的请求地址是welcome.html,就能直接从handlerMap获取到。

             <entry key="welcome.html"><ref bean="mainController"/></entry>
             <entry key="oth*.html"><ref bean="otherController"/></entry>

    但如果请求的是other.html,这个时候由于在handlerMap注册的urlPath是oth*.html,不能直接获取到,所以就需要走下一步。

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
   // Direct match?
   Object handler = this.handlerMap.get(urlPath);
   if (handler != null) {
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }

   // Pattern match?
   List<String> matchingPatterns = new ArrayList<>();
   for (String registeredPattern : this.handlerMap.keySet()) {
      if (getPathMatcher().match(registeredPattern, urlPath)) {
         matchingPatterns.add(registeredPattern);
      }
      else if (useTrailingSlashMatch()) {
         if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
            matchingPatterns.add(registeredPattern +"/");
         }
      }
   }

   String bestMatch = null;
   Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
   if (!matchingPatterns.isEmpty()) {
      matchingPatterns.sort(patternComparator);
      bestMatch = matchingPatterns.get(0);
   }
   if (bestMatch != null) {
      handler = this.handlerMap.get(bestMatch);
      if (handler == null) {
         if (bestMatch.endsWith("/")) {
            handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
         }
         if (handler == null) {
            throw new IllegalStateException(
                  "Could not find handler for best pattern match [" + bestMatch + "]");
         }
      }
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
      Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
      for (String matchingPattern : matchingPatterns) {
         if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
            Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
            Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
            uriTemplateVariables.putAll(decodedVars);
         }
      }
      return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
   }

   // No handler found...
   return null;
}
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
      String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
   HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
   chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
   if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
      chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
   }
   return chain;
}

      这里主要分为三部分,第一部分就是正常获取handler例如welcome.html,并通过buildPathExposingHandler方法产生一个HandlerExecutionChain 直接返回。如果不能直接获取到,就遍历handlerMap,进行模糊匹配,通过getPathMatcher().match(registeredPattern, urlPath)方法,如果成功就添加到matchingPatterns中,如果失败就再走下面的useTrailingSlashMatch()

public boolean useTrailingSlashMatch() {
   return this.useTrailingSlashMatch;
}
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
   matchingPatterns.add(registeredPattern +"/");
}

    这个值你能在.xml文间中设置,例如:

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="defaultHandler"><ref bean="starController"/></property>
   <property name="rootHandler"><ref bean="mainController"/></property>
   <property name="useTrailingSlashMatch" value="true"/>
   <property name="urlMap">
      ...............
   </property>
</bean>

     这个值表示的意思就是使不使用未斜杠匹配,通过前面的源码我们可以知道路径的匹配是进行字符串的比较,所以每个字符符号都用意义。例如你如果请求 http://localhost:8080/demo1/spring/hello,这个地址是正确的,在换一下,在结尾加“/”:http://localhost:8080/demo1/spring/hello/,这个时候如果useTrailingSlashMatch还是默认的false的话,就会报404,如果useTrailingSlashMatch为true,就能正确请求到目标,这个通过源码我们也能理解。

     通过模糊匹配,由于是模糊,其就可能有多个,所以这里就会进行优先级,然后找到一个最符合的。确定到对应的handler后,会判断是不是String,在前面abstractHandlerMapping我们有梳理过,一个Handler可以使用延迟加载。现在就会在这里去加载到handler了。获取到handler后(validateHandler(handler, request) 为空方法),会通过

String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

获取到标准映射,这里会用到Spring的一个工具方法去切割

 String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true);

通过这个去试下看其是怎样切割的:     String[] sa = StringUtils.tokenizeToStringArray("a,b , ,c", ",",  true, false) 。

public String extractPathWithinPattern(String pattern, String path) {
   String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true);
   String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
   StringBuilder builder = new StringBuilder();
   boolean pathStarted = false;

   for (int segment = 0; segment < patternParts.length; segment++) {
      String patternPart = patternParts[segment];
      if (patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) {
         for (; segment < pathParts.length; segment++) {
            if (pathStarted || (segment == 0 && !pattern.startsWith(this.pathSeparator))) {
               builder.append(this.pathSeparator);
            }
            builder.append(pathParts[segment]);
            pathStarted = true;
         }
      }
   }
   return builder.toString();
}

   同时这里handlerMap中所以的urlPath都是有"/"的,不管你有没有配置,这个可以在AbstractUrlHandlerMapping的子类看下。

                                   

        

之后还有两个方法:

Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);

其中getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath)方法是提取url中模块参数,这个我们看下Spring源码中其的测试demo就可以知道其的作用了:

Map<String, String> result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}", "/hotels/1");
assertEquals(Collections.singletonMap("hotel", "1"), result);

result = pathMatcher.extractUriTemplateVariables("/h?tels/{hotel}", "/hotels/1");
assertEquals(Collections.singletonMap("hotel", "1"), result);

result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}/bookings/{booking}", "/hotels/1/bookings/2");
Map<String, String> expected = new LinkedHashMap<>();
expected.put("hotel", "1");
expected.put("booking", "2");
assertEquals(expected, result);

而我们再xml的使用:

<map>
   <entry key="welcome*"><ref bean="otherController"/></entry>
   <entry key="/hotels/{hotel}"><ref bean="mainController"/></entry>
</map>

然后模拟请求:MockHttpServletRequest req = new MockHttpServletRequest("GET", "/hotels/你好吗");

              

之后是getUrlPathHelper().decodePathVariables(方法是通过解码(根据对应的字符编码,request中的characterEncoding)方法获取对应的模板参数。关于这个方法,我们知道我们在url栏中输入汉字,其在请求的时候并不是直接传入汉字,而是对应的汉字asicc码,同时你如果传入空格也是会有转换的,例如你在地址栏输入:

                     

但其在请求时是传入:

              

decodePathVariables 方法最后是调用Utils.decode("", CHARSET))方法去处理,我们看下测试demo:

@Test
public void decode() throws UnsupportedEncodingException {
   assertEquals("Invalid encoded URI", "", UriUtils.decode("", CHARSET));
   assertEquals("Invalid encoded URI", "\u4f60\u597d", UriUtils.decode("你好", CHARSET));
   assertEquals("Invalid encoded URI", "foobar", UriUtils.decode("foobar", CHARSET));
   assertEquals("Invalid encoded URI", "foo bar", UriUtils.decode("foo%20bar", CHARSET));
   assertEquals("Invalid encoded URI", "foo+bar", UriUtils.decode("foo%2bbar", CHARSET));
}

例如我们将前面的模拟请求改下:

MockHttpServletRequest req = new MockHttpServletRequest("GET", "/hotels/%E4%BD%A0%E5%A5%BD");
req.setCharacterEncoding("UTF-8");

                    

public Map<String, String> decodePathVariables(HttpServletRequest request, Map<String, String> vars) {
   if (this.urlDecode) {
      return vars;
   }
   else {
      Map<String, String> decodedVars = new LinkedHashMap<>(vars.size());
      for (Entry<String, String> entry : vars.entrySet()) {
         decodedVars.put(entry.getKey(), decodeInternal(request, entry.getValue()));
      }
      return decodedVars;
   }
}
private String decodeInternal(HttpServletRequest request, String source) {
   String enc = determineEncoding(request);
   try {
      return UriUtils.decode(source, enc);
   }
     ..........
      return URLDecoder.decode(source);
   }
}

不过要注意的是,这个使用了UrlPathHelper的成员变量urlDecode,这个urlDecode默认是为true的,所以一般这里会跳过,如果要使用这个的话,需要自动去配置这个:

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="urlDecode"><value>true</value></property>
     ..........

  这两个方法之后是调用前面讲到构建HandlerExecutionChain的buildPathExposingHandler方法

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
      String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {

   HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
   chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
   if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
      chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
   }
   return chain;
}

这里构建了两个Interceptor(如果不为空的话):UriTemplateVariablesHandlerInterceptor、PathExposingHandlerInterceptor,都继承与HandlerInterceptorAdapter

private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {
   private final String bestMatchingPattern;
   private final String pathWithinMapping;
   public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
      this.bestMatchingPattern = bestMatchingPattern;
      this.pathWithinMapping = pathWithinMapping;
   }
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
      exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
      request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
      return true;
   }
}
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {
   private final Map<String, String> uriTemplateVariables;
   public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
      this.uriTemplateVariables = uriTemplateVariables;
   }
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
      exposeUriTemplateVariables(this.uriTemplateVariables, request);
      return true;
   }
}

这里我们梳理了关于AbstractUrlHandlerMapping抽象类的两个重要的地方,handlerMap的注册,以及通过通过request获取定handler的过程,现在我们来看其的实现类。

     SimpleUrlHandlerMapping

      这个我们举例用到就是这个 

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="defaultHandler"><ref bean="starController"/></property>
   <property name="rootHandler"><ref bean="mainController"/></property>
   <property name="useTrailingSlashMatch" value="true"/>
   <property name="urlDecode"><value>true</value></property>
   <property name="urlMap">
      <map>
         <entry key="welcome*"><ref bean="otherController"/></entry>
           ...........
         <entry key="/hotels/{hotel}"><ref bean="mainController"/></entry>
      </map>
   </property>
</bean>
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
   private final Map<String, Object> urlMap = new LinkedHashMap<>();
    ................
   @Override
   public void initApplicationContext() throws BeansException {
      super.initApplicationContext();
      registerHandlers(this.urlMap);
   }

   protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
      if (urlMap.isEmpty()) {
         logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
      }
      else {
         urlMap.forEach((url, handler) -> {
            // Prepend with slash if not already present.
            if (!url.startsWith("/")) {
               url = "/" + url;
            }
            // Remove whitespace from handler bean name.
            if (handler instanceof String) {
               handler = ((String) handler).trim();
            }
            registerHandler(url, handler);
         });
      }
   }
}

可以看到其主要是registerHandlers方法,去注册url,与handler的关系,这个registerHandler(url, handler)方法就会调用到其父类我们刚才梳理的AbstractUrlHandlerMapping的registerHandler方法。

可以看到这里的成员变量urlMap 的内容就是我们在xml中配置的

<property name="urlMap">
   <map>
      <entry key="welcome*"><ref bean="otherController"/></entry>
        ..........
      <entry key="/hotels/{hotel}"><ref bean="mainController"/></entry>
   </map>
</property>

同时这里也填了一个前面讲的一个地方,不管你有没有在xml中加"/",最好到handlerMap最前面都有"/"。

然后这个registerHandlers方法是通过initApplicationContext方法的调用,同时我们也知道AbstractHandlerMapping是有实现ApplicationContextAware接口,然后这个initApplicationContext方法就是在设置ApplicationContext是调用的:

@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
    ...............
   else if (this.applicationContext == null) {
        ............
      this.applicationContext = context;
      this.messageSourceAccessor = new MessageSourceAccessor(context);
      initApplicationContext(context);
   }
      ...........
}

       BeanNameUrlHandlerMapping

   BeanNameUrlHandlerMapping继承与AbstractDetectingUrlHandlerMapping,而AbstractDetectingUrlHandlerMapping继承与AbstractUrlHandlerMapping:

public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
   private boolean detectHandlersInAncestorContexts = false;
   .........
   @Override
   public void initApplicationContext() throws ApplicationContextException {
      super.initApplicationContext();
      detectHandlers();
   }
    .............
   protected void detectHandlers() throws BeansException {
      ApplicationContext applicationContext = obtainApplicationContext();
       ...........
      String[] beanNames = (this.detectHandlersInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
            applicationContext.getBeanNamesForType(Object.class));

      // Take any bean name that we can determine URLs for.
      for (String beanName : beanNames) {
         String[] urls = determineUrlsForHandler(beanName);
         if (!ObjectUtils.isEmpty(urls)) {
            // URL paths found: Let's consider it a handler.
            registerHandler(urls, beanName);
         }
         else {
            if (logger.isDebugEnabled()) {
               logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
            }
         }
      }
   }
    .........
   protected abstract String[] determineUrlsForHandler(String beanName);
}

可以看到其有一个抽象方法determineUrlsForHandler,BeanNameUrlHandlerMapping就是实现这个determineUrlsForHandler

方法:

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
   
   @Override
   protected String[] determineUrlsForHandler(String beanName) {
      .........
   }
}

所以BeanNameUrlHandlerMapping是与上面的SimpleUrlHandlerMapping方法类似通过调用initApplicationContext,再调用detectHandlers去将其的handler注册到handlerMap中去。

   我们在前面梳理BeanNameUrlHandlerMapping的使用时用的配置是:

<beans>
   <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
   <bean id="godCtrl" name="/ /mypath/welcome.html /mypath/show.html /mypath/bookseats.html /mypath/reservation.html /mypath/payment.html /mypath/confirmation.html /mypath/test*"
         class="java.lang.Object"/>
</beans>

然后我们来看detectHandlers方法,其首先是调用的 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class))方法,可以看到其传入的类型时Object.class,所以其实这个就是获取applicationContext中使用的Bean了。

在循环遍历beanName:调用determineUrlsForHandler方法:

protected String[] determineUrlsForHandler(String beanName) {
   List<String> urls = new ArrayList<>();
   if (beanName.startsWith("/")) {
      urls.add(beanName);
   }
   String[] aliases = obtainApplicationContext().getAliases(beanName);
   for (String alias : aliases) {
      if (alias.startsWith("/")) {
         urls.add(alias);
      }
   }
   return StringUtils.toStringArray(urls);
}

可以看到这个方法就是判断一个beanName或者其别名是不是以"/"开头,如果是就加入到urls,再返回给detectHandlers方法,之后再将其put到AbstractUrlHandlerMapping的handlerMap中。

   2、AbstractHandlerMethodMapping

         与上面的AbstractUrlHandlerMapping类似,我们也从其关于handler映射关系的注册以及获取来梳理其逻辑。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
      .............
   private boolean detectHandlerMethodsInAncestorContexts = false;
   @Nullable
   private HandlerMethodMappingNamingStrategy<T> namingStrategy;

   private final MappingRegistry mappingRegistry = new MappingRegistry();
      ...........
   @Override
   public void afterPropertiesSet() {
      initHandlerMethods();
   }
   protected void initHandlerMethods() {
      String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
            obtainApplicationContext().getBeanNamesForType(Object.class));
      for (String beanName : beanNames) {
                   ........
            if (beanType != null && isHandler(beanType)) {
               detectHandlerMethods(beanName);
            }
         }
      }
      handlerMethodsInitialized(getHandlerMethods());
   }

   protected void detectHandlerMethods(final Object handler) {
      Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

      if (handlerType != null) {
         final Class<?> userType = ClassUtils.getUserClass(handlerType);
              .............
            registerHandlerMethod(handler, invocableMethod, mapping);
         }
      }
   }
   
   protected HandlerMethod createHandlerMethod(Object handler, Method method) {
      HandlerMethod handlerMethod;
      if (handler instanceof String) {
         String beanName = (String) handler;
         handlerMethod = new HandlerMethod(beanName,
               obtainApplicationContext().getAutowireCapableBeanFactory(), method);
      }
      else {
         handlerMethod = new HandlerMethod(handler, method);
      }
      return handlerMethod;
   }
    ..........
   @Override
   protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
      String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
          .........
         HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            ...........
         return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
      }
      finally {
         this.mappingRegistry.releaseReadLock();
      }
   }
    ..........
   protected abstract boolean isHandler(Class<?> beanType);
   @Nullable
   protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
    ..........
   @Nullable
   protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
     .............
   class MappingRegistry {
       ............
      public void register(T mapping, Object handler, Method method) {
         this.readWriteLock.writeLock().lock();
         try {
            HandlerMethod handlerMethod = createHandlerMethod(handler, method);
            assertUniqueMethodMapping(handlerMethod, mapping);

            this.mappingLookup.put(mapping, handlerMethod);
          ...........
      }
   ..................
}

        我们可以看到AbstractHandlerMethodMapping的初始化注册是通过afterPropertiesSet调用initHandlerMethods进行的(AbstractUrlHandlerMapping是通过ApplicationContextAware进行的)。

@Override
public void afterPropertiesSet() {
   initHandlerMethods();
}
protected void initHandlerMethods() {
   String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
         obtainApplicationContext().getBeanNamesForType(Object.class));
   for (String beanName : beanNames) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
         Class<?> beanType = null;
         try {
            beanType = obtainApplicationContext().getType(beanName);
         }
           ..........
         if (beanType != null && isHandler(beanType)) {
            detectHandlerMethods(beanName);
         }
      }
   }
   handlerMethodsInitialized(getHandlerMethods());
}
private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";

这里有一个参数detectHandlerMethodsInAncestorContexts(从不从其的祖先搜索,默认是false,我们知道Factory可以有实现HierarchicalBeanFactory接口,可以设置其的parent):

public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type) {
   String[] result = lbf.getBeanNamesForType(type);
   if (lbf instanceof HierarchicalBeanFactory) {
      HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
      if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
         String[] parentResult = beanNamesForTypeIncludingAncestors(
               (ListableBeanFactory) hbf.getParentBeanFactory(), type);
         List<String> resultList = new ArrayList<>();
         resultList.addAll(Arrays.asList(result));
         for (String beanName : parentResult) {
            if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
               resultList.add(beanName);
            }
         }
         result = StringUtils.toStringArray(resultList);
      }
   }
   return result;
}

  可以看到这里的beanType是Objecy.class,所以是获取BeanFactory中所有的beanName。

     获取beanName后就遍历,条件时beanName不以"scopedTarget."开头,这个就是表示bean 是ScopedProxy,这个ScopedProxy,前面已经梳理过了好像,在通过BeanFactory获取bean的时候。简单来说,就是bean的作用域,例如单例、多例,request、session这种。而request这种是一次连接创建一次,这种就会用ScopedProxy。

      然后beanName获取对应类型beanType,再判断其是不是一个handler,通过isHandler(beanType),其实一个抽象方法:

protected abstract boolean isHandler(Class<?> beanType);

     如果为true 则调用detectHandlerMethods(beanName):

protected void detectHandlerMethods(final Object handler) {
   Class<?> handlerType = (handler instanceof String ?
         obtainApplicationContext().getType((String) handler) : handler.getClass());
   if (handlerType != null) {
      final Class<?> userType = ClassUtils.getUserClass(handlerType);
      Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
            (MethodIntrospector.MetadataLookup<T>) method -> {
               try {
                  return getMappingForMethod(method, userType);
               }
                .........
            });
      for (Map.Entry<Method, T> entry : methods.entrySet()) {
         Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
         T mapping = entry.getValue();
         registerHandlerMethod(handler, invocableMethod, mapping);
      }
   }
}

   首先获取其类型,然后

public static Class<?> getUserClass(Class<?> clazz) {
   if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
      Class<?> superclass = clazz.getSuperclass();
      if (superclass != null && Object.class != superclass) {
         return superclass;
      }
   }
   return clazz;
}
/** The CGLIB class separator: "$$" */
public static final String CGLIB_CLASS_SEPARATOR = "$$";

这个方法就是获取其实际的类,去掉前缀描叙。然后调用MethodIntrospector.selectMethods方法:

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
   final Map<Method, T> methodMap = new LinkedHashMap<>();
   Set<Class<?>> handlerTypes = new LinkedHashSet<>();
   Class<?> specificHandlerType = null;
   if (!Proxy.isProxyClass(targetType)) {
      handlerTypes.add(targetType);
      specificHandlerType = targetType;
   }
   handlerTypes.addAll(Arrays.asList(targetType.getInterfaces()));

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

      ReflectionUtils.doWithMethods(currentHandlerType, method -> {
         Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
         T result = metadataLookup.inspect(specificMethod);
         if (result != null) {
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
            if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
               methodMap.put(specificMethod, result);
            }
         }
      }, ReflectionUtils.USER_DECLARED_METHODS);
   }

   return methodMap;
}
(MethodIntrospector.MetadataLookup<T>) method -> {
   try {
      return getMappingForMethod(method, userType);
   }
   catch (Throwable ex) {
      throw new IllegalStateException("Invalid mapping on handler class [" +
            userType.getName() + "]: " + method, ex);
   }
});

    首先判断其是不是JDK动态代理类,如果不是就添加到handlerTypes中,再将这个targetType实现的接口也添加到handlerTypes中并且设置specificHandlerType,然后遍历。

    然后整个这里比较绕(如果是一般情况也不复杂):

final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

ReflectionUtils.doWithMethods(currentHandlerType, method -> {
   Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
   T result = metadataLookup.inspect(specificMethod);
   if (result != null) {
      Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
      if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
         methodMap.put(specificMethod, result);
      }
   }
}, ReflectionUtils.USER_DECLARED_METHODS);

这里的metadataLookup.inspect就是前面的lamda表达式,实际调用的就是前面的,其是一个抽象类,需要子类实现(这个方法比较重要)

   return getMappingForMethod(method, userType)

protected abstract T getMappingForMethod(Method method, Class<?> handlerType);

这里的ClassUtils.getMostSpecificMethod(method, targetClass),就是获取具体的方法,这个

public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
   if (isOverridable(method, targetClass) &&
         targetClass != null && targetClass != method.getDeclaringClass()) {
      try {
         if (Modifier.isPublic(method.getModifiers())) {
            try {
               return targetClass.getMethod(method.getName(), method.getParameterTypes());
            }.......
         }
         else {
            Method specificMethod =
                  ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
            return (specificMethod != null ? specificMethod : method);
         }
      }..........
   }
   return method;
}

这里如果是一般情况直接返回原method。但如果是静态代理这种,其需要返回代理类的方法。用一个例子来说明:    

public interface Car {
   String getName();
}
@Controller
public class CarProxy implements Car {

   private Car car;
   @Override
   @RequestMapping("/car")
   public String getName() {
      System.out.println("Execution CarProxy log: ");
      return car.getName();
   }
   public void setCar(Car car) {
      this.car = car;
   }
}

然后我们来看这个过程:

   先将specificHandlerType设置为CarProxy.class,在将两个都加入到handlerTypes中:

                    

        

    遍历调用ReflectionUtils.doWithMethods,再调用ClassUtils.getMostSpecificMethod(method, targetClass),这个

   targetClass一直是Carproxy  (targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType)),所以在处理Carproxy 的时候由于 targetClass != method.getDeclaringClass() 为false所以返回的为原Carproxy 的getName方法,当遍历到Car的时候,由于targetClass为Carproxy.class,所以其实际也是Carproxy的getName方法(这才是正确的,因为我们实际就是需要调用Carproxy)。

       

             

        

     这里梳理后我们再回到前面的selectMethods方法,获取specificMethod后,再通过metadataLookup.inspect(specificMethod)获取result,如果不为空(与前面的抽象方法getMappingForMethod有关联),然后获取该specificMethod的桥方法(这里主要是针对泛型的)。

Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

findBridgedMethod方法调用,如果specificMethod不是桥方法就直接返回specificMethod本身。如果是桥方法,就去获取其的准确方法(没有抹掉参数类型)。然后添加到methodMap返回。

    再回到前面的detectHandlerMethods方法, 获取到methods之后再遍历将对应关系注册。

for (Map.Entry<Method, T> entry : methods.entrySet()) {
   Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
   T mapping = entry.getValue();
   registerHandlerMethod(handler, invocableMethod, mapping);
}

  获取到invocableMethod,调用registerHandlerMethod:

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
   this.mappingRegistry.register(mapping, handler, method);
}
private final MappingRegistry mappingRegistry = new MappingRegistry();
class MappingRegistry {
   private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
   private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
   private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
   private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
   private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
   private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 .......
}
public void register(T mapping, Object handler, Method method) {
   this.readWriteLock.writeLock().lock();
   try {
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);
      assertUniqueMethodMapping(handlerMethod, mapping);
         ........
      this.mappingLookup.put(mapping, handlerMethod);
      List<String> directUrls = getDirectUrls(mapping);
      for (String url : directUrls) {
         this.urlLookup.add(url, mapping);
      }
      String name = null;
      if (getNamingStrategy() != null) {
         name = getNamingStrategy().getName(handlerMethod, mapping);
         addMappingName(name, handlerMethod);
      }
        ............
      this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
   }
   finally {
      this.readWriteLock.writeLock().unlock();
   }
}

这里使用了可重入的读写锁ReentrantReadWriteLock,通过createHandlerMethod方法,构建一个HandlerMethod:

protected HandlerMethod createHandlerMethod(Object handler, Method method) {
   HandlerMethod handlerMethod;
   if (handler instanceof String) {
      String beanName = (String) handler;
      handlerMethod = new HandlerMethod(beanName,
            obtainApplicationContext().getAutowireCapableBeanFactory(), method);
   }
   else {
      handlerMethod = new HandlerMethod(handler, method);
   }
   return handlerMethod;
}
public HandlerMethod(Object bean, Method method) {
   this.bean = bean;
   this.beanFactory = null;
   this.beanType = ClassUtils.getUserClass(bean);
   this.method = method;
   this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
   this.parameters = initMethodParameters();
   evaluateResponseStatus();
}
private MethodParameter[] initMethodParameters() {
   int count = this.bridgedMethod.getParameterCount();
   MethodParameter[] result = new MethodParameter[count];
   for (int i = 0; i < count; i++) {
      HandlerMethodParameter parameter = new HandlerMethodParameter(i);
      GenericTypeResolver.resolveParameterType(parameter, this.beanType);
      result[i] = parameter;
   }
   return result;
}

   这个HandlerMethod就是面试处理方法的,例如这个方法有哪些参数等,再通过assertUniqueMethodMapping(handlerMethod,,mapping)方法判断handlerMethod与mapping是不是唯一映射关系,再将这个关系put到mappingLookup中。

  然后是getDirectUrls,这个到其子类的时候再梳理,其与(getMappingForMethod相关)(就是看一个mapping有多少地址与其对应),将其添加到urlLookup中。之后再看有没有设置namingStrategy,如果有,就根据handlerMethod与mapping,获取对应的name,建立name与handlerMethod的关系,再通过addMappingName将其put到nameLookup中

(Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>())。

   然后再将mapping与MappingRegistration建立联系,将其put到registry 中

    (Map<T, MappingRegistration<T>> registry = new HashMap<>())。

至此,handleMethod与mapping就完成注册了。下面我们再来看下获取handler的 getHandlerInternal方法:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   this.mappingRegistry.acquireReadLock();
   try {
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
   }
   finally {
      this.mappingRegistry.releaseReadLock();
   }
}

这个通过request获取对应handler路径与前面AbstractUrlHandlerMapping的一样,通过UrlPathHelper去获取,现在看lookupHandlerMethod方法:

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   List<Match> matches = new ArrayList<>();
   List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
   if (directPathMatches != null) {
      addMatchingMappings(directPathMatches, matches, request);
   }
   if (matches.isEmpty()) {
      // No choice but to go through all mappings...
      addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
   }

   if (!matches.isEmpty()) {
      Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
      matches.sort(comparator);
      Match bestMatch = matches.get(0);
      if (matches.size() > 1) {
         ............
         Match secondBestMatch = matches.get(1);
         if (comparator.compare(bestMatch, secondBestMatch) == 0) {
            Method m1 = bestMatch.handlerMethod.getMethod();
            Method m2 = secondBestMatch.handlerMethod.getMethod();
            throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                  request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
         }
      }
      handleMatch(bestMatch.mapping, lookupPath, request);
      return bestMatch.handlerMethod;
   }
   else {
      return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
   }
}
@Nullable
protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
      throws Exception {

   return null;
}

     首先是根据lookupPath获取urlLookup的内容:

public List<T> getMappingsByUrl(String urlPath) {
   return this.urlLookup.get(urlPath);
}

    然后是用directPathMatches去与request匹配,如果配置就添加到matches中,如果不能获取就用当前的所有mappingLookup去匹配。

if (directPathMatches != null) {
   addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
   // No choice but to go through all mappings...
   addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
public Map<T, HandlerMethod> getMappings() {
   return this.mappingLookup;
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
   for (T mapping : mappings) {
      T match = getMatchingMapping(mapping, request);
      if (match != null) {
         matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
      }
   }
}

这个getMatchingMapping是抽象方法,到子类我们看其是怎样匹配的。

private class Match {
   private final T mapping;
   private final HandlerMethod handlerMethod;
   public Match(T mapping, HandlerMethod handlerMethod) {
      this.mapping = mapping;
      this.handlerMethod = handlerMethod;
   }
   @Override
   public String toString() {
      return this.mapping.toString();
   }
}

可以看到这个Match就是放mapping与其对应的HandlerMethod的,前面梳理的时候也一种看到这个mapping,其为泛型,同样到子类再看其是什么。

  再回到前面的lookupHandlerMethod方法,将这些Match添加到matches后。到后面会对其进行排序,如果有多个,当第一与第二排序的结果是相等的就报不是唯一匹配的:

if (comparator.compare(bestMatch, secondBestMatch) == 0) {
   Method m1 = bestMatch.handlerMethod.getMethod();
   Method m2 = secondBestMatch.handlerMethod.getMethod();
   throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
         request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}

获取到对应的Match后,调用handleMatch方法:

protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
   request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
}
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

 再从Match中获取对应的HandlerMethod返回。

    RequestMappingInfoHandlerMapping、RequestMappingHandlerMapping

   现在我们来梳理AbstractHandlerMethodMapping的子类RequestMappingInfoHandlerMapping,以及AbstractHandlerMethodMapping的子类RequestMappingHandlerMapping

         RequestMappingInfoHandlerMapping

public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
   ...............
    protected RequestMappingInfoHandlerMapping() {
       setHandlerMethodMappingNamingStrategy(new RequestMappingInfoHandlerMethodMappingNamingStrategy());
   }
   @Override
   protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
      return info.getMatchingCondition(request);
   }
   
   @Override
   protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
      return (info1, info2) -> info1.compareTo(info2, request);
   }

   @Override
   protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
      super.handleMatch(info, lookupPath, request);
         ...........
   }
    ...........
   private Map<String, MultiValueMap<String, String>> extractMatrixVariables(
         HttpServletRequest request, Map<String, String> uriVariables) {

      Map<String, MultiValueMap<String, String>> result = new LinkedHashMap<>();
      for (Entry<String, String> uriVar : uriVariables.entrySet()) {
            ............
         result.put(uriVar.getKey(), getUrlPathHelper().decodeMatrixVariables(request, vars));
      }
      return result;
   }

   @Override
   protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath,
         HttpServletRequest request) throws ServletException {
      PartialMatchHelper helper = new PartialMatchHelper(infos, request);
        ........
      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);
      }
      ...........
      return null;
   }
     ...........
}

首先可以看到这个RequestMappingInfoHandlerMapping也是一个抽象类,可以看到其初始化的时候是默认设置了NamingStrategy  RequestMappingInfoHandlerMethodMappingNamingStrategy。

     RequestMappingHandlerMapping

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
      implements MatchableHandlerMapping, EmbeddedValueResolverAware {
   private boolean useSuffixPatternMatch = true;
   private boolean useRegisteredSuffixPatternMatch = false;
   private boolean useTrailingSlashMatch = true;
    ............
   private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
     .............
   @Override
   public void afterPropertiesSet() {
      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();
   }
       ...........
   @Override
   protected boolean isHandler(Class<?> beanType) {
      return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
   }
   @Override
   @Nullable
   protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
      RequestMappingInfo info = createRequestMappingInfo(method);
      if (info != null) {
         RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
         if (typeInfo != null) {
            info = typeInfo.combine(info);
         }
      }
      return info;
   }
        
   @Nullable
   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);
   }
  
   @Nullable
   protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
      return null;
   }
   @Nullable
   protected RequestCondition<?> getCustomMethodCondition(Method method) {
      return null;
   }
             .........
   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();
   }
   protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {
      ..........
   }
   @Override
   public RequestMatchResult match(HttpServletRequest request, String pattern) {
      RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build();
      RequestMappingInfo matchingInfo = info.getMatchingCondition(request);
        .......
      return new RequestMatchResult(patterns.iterator().next(), lookupPath, getPathMatcher());
   }
      ............
}

      1、首先看下前面说的抽象方法isHandler(RequestMappingHandlerMapping)

if (beanType != null && isHandler(beanType)) {
   detectHandlerMethods(beanName);
}
@Override
protected boolean isHandler(Class<?> beanType) {
   return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
         AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

    可以看到判断是不是Handler是看beanType有没有@Controller、@RequestMapping (@RestController有加@Controller注解):

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
   @AliasFor(annotation = Controller.class)
   String value() default "";
}

     2、抽象方法getMappingForMethod(RequestMappingHandlerMapping)

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);
         }
      });
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
   RequestMappingInfo info = createRequestMappingInfo(method);
   if (info != null) {
      RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      if (typeInfo != null) {
         info = typeInfo.combine(info);
      }
   }
   return info;
}

这个handlerType按前面的梳理就是对应加了@Controller注解的bean的class描叙,method就是循环遍历的handlerType中的方法,前面我们提到过要注意这里的返回为不为

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

为什么传入的是Method,但这里用的是AnnotatedElement,这里在前面文章介绍Class、Method、Field的结构的时候有分析过,简答说下就是Method有实现AnnotatedElement接口(用于描叙注解的内容),可以看到这里的判断是方法上面有没有注解@RequestMapping,如果没有就返回空,就不注册当前Method为一个HanlderMethod的,之后还会获取RequestCondition

@Nullable
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
   return null;
}
protected RequestCondition<?> getCustomMethodCondition(Method method) {
   return null;
}

可以看到这两个方法是返回空的,应该是给开发者自定义的(Custom)

public interface RequestCondition<T> {
   T combine(T other);
   @Nullable
   T getMatchingCondition(HttpServletRequest request);
   int compareTo(T other, HttpServletRequest request);
}

如果有@RequestMapping注解就createRequestMappingInfo创建RequestMappingInfo返回

下面的config是什么时候初始化的呢,是通过其实现的InitializingBean接口的的afterPropertiesSet方法:

@Override
public void afterPropertiesSet() {
   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();
}
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();
}
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();

  可以看到这里实现是构建一个RequestMappingInfo.Builder,将@RequestMapping的属性值添加到Builder,然后通过build()返回RequestMappingInfo:

public RequestMappingInfo build() {
   ContentNegotiationManager manager = this.options.getContentNegotiationManager();

   PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
         this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
         this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
         this.options.getFileExtensions());

   return new RequestMappingInfo(this.mappingName, patternsCondition,
         new RequestMethodsRequestCondition(this.methods),
         new ParamsRequestCondition(this.params),
         new HeadersRequestCondition(this.headers),
         new ConsumesRequestCondition(this.consumes, this.headers),
         new ProducesRequestCondition(this.produces, this.headers, manager),
         this.customCondition);
}

  现在我们就知道了这个RequestMappingInfo有什么了(可以简单的将其看作为RequestMapping的描叙),同时@RequestMapping的属性对应了一个RequestCondition,同时还可以自定义一个RequestCondition(这个RequestCondition有什么用)。

    我们再回到前面的getMappingForMethod方法,获取了Method的RequestMappingInfo后,再获取handlerType的,如果Class上面也有@RequestMapping,就合并这两个typeInfo.combine(info):

@Override
public RequestMappingInfo combine(RequestMappingInfo other) {
   String name = combineNames(other);
   PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
   RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
   ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
   HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
   ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
   ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
   RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);

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

       这里就有了RequestCondition的第一个用处,调用其的combine将两个相同类型的RequestCondition合并,例如PatternsRequestCondition的combine方法,方法测试demo

@Test
public void combineMultiplePatterns() {
   PatternsRequestCondition c1 = new PatternsRequestCondition("/t1", "/t2");
   PatternsRequestCondition c2 = new PatternsRequestCondition("/m1", "/m2");

   assertEquals(new PatternsRequestCondition("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"), c1.combine(c2));
}

这个patterns就是对应RequestMapping的path属性或者value:

 paths( resolveEmbeddedValuesInPatterns(requestMapping.path() 。value的值会处理到path中(但value与path只能有一个):

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
   String name() default "";
   @AliasFor("path")
   String[] value() default {};
   @AliasFor("value")
   String[] path() default {};
   RequestMethod[] method() default {};
   String[] params() default {};
   String[] headers() default {};
   String[] consumes() default {};
   String[] produces() default {};
}

同时有两个会报错:

@RequestMapping(value = "/car",path = "/carProxy")

      现在RequestMappingInfoHandlerMapping具体实现类RequestMappingHandlerMapping的主要方法就梳理的,我们再来其本身的重写或者实现的的一些主要方法:

      1、getDirectUrls方法与getMappingPathPatterns方法(RequestMappingInfoHandlerMapping)

   这个前面有挖这两个坑,现在来填:

public void register(T mapping, Object handler, Method method) {
      .............
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);
       ...............
      this.mappingLookup.put(mapping, handlerMethod);
      List<String> directUrls = getDirectUrls(mapping);
      for (String url : directUrls) {
         this.urlLookup.add(url, mapping);
      }
     .............
}

    这里就是前面在将注册HandlerMethod的时候有调用,其中mapping就是前面通过getMappingForMethod方法返回的RequestMappingInfo

private List<String> getDirectUrls(T mapping) {
   List<String> urls = new ArrayList<>(1);
   for (String path : getMappingPathPatterns(mapping)) {
      if (!getPathMatcher().isPattern(path)) {
         urls.add(path);
      }
   }
   return urls;
}
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
   return info.getPatternsCondition().getPatterns();
}

所以这个getMappingPathPatterns方法就是获取RequestMappingInfo的patterns(Reqeuestmapping的path),同时这里可以补充说明下关于urlLookup:

private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
@Override
public void add(K key, @Nullable V value) {
   List<V> values = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>());
   values.add(value);
}

其实一个List,就是说一个地址能对应多个RequestMappingInfo。然后联系到前面lookupHandlerMethod方法通过lookupPath

找对应的HandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   List<Match> matches = new ArrayList<>();
   List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
     ..........

   if (!matches.isEmpty()) {
      Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
      matches.sort(comparator);
     
      Match bestMatch = matches.get(0);
      ..............
         Match secondBestMatch = matches.get(1);
        ...............
} 

这里有一个比比较器MatchComparator,其最终比较的是RequestMappingInfo(比较的其实就是@ReqesutMapping的属性对应的RequestCondition)

@Override
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.paramsCondition.compareTo(other.getParamsCondition(), request);
   if (result != 0) {
      return result;
   }
   ..........
   return 0;
}

这里就是目前RequestCondition的第二个使用,这部分其实用的就是例如:同一个url,虽然其请求的地址一样,但地址可以表示POST,也可以表示GET,也可能有不同的参数等。

   2、getMatchingMapping方法

    这个是在前面讲addMatchingMappings的时候挖的:

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
   for (T mapping : mappings) {
      T match = getMatchingMapping(mapping, request);
      if (match != null) {
         matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
      }
   }
}
public Map<T, HandlerMethod> getMappings() {
   return this.mappingLookup;
}
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
   return info.getMatchingCondition(request);
}

其就是通过request获取到对应的RequestMappingInfo,然后建立ReqeustMappingInfo与对应的HandlerMethod的关系,将其包装为Match。然后在联系前面的MatchComparator比较器,其就是去Match中的RequestMappingInfo

private class MatchComparator implements Comparator<Match> {
    ..........
   @Override
   public int compare(Match match1, Match match2) {
      return this.comparator.compare(match1.mapping, match2.mapping);
   }
}

     至此,整个处理器映射器(HandlerMapping)的使用及其主要流程的源码细节就梳理完毕了。

 

  

   

 

    

   

 

   

    

   

   

  

   

 

 

 

    

  

  

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值