SpringMVC方法映射原理解析

March 11, 2022

HandlerMapping接口

package:spring-webmvc。

顶级接口。

用于定义请求和处理对象之间的映射关系。

主要方法:

1
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

主要实现类:HandlerMapping(接口)-> AbstractHandlerMapping(抽象类) -> AbstractHandlerMethodMapping(抽象类) ->...省略... -> RequestMappingHandlerMapping(实现类)

WebMvcAutoConfiguration自动配置类

声明了一个 RequestMappingHandlerMapping bean

1
2
3
4
5
6
7
8
9
10
11
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(
      @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
      @Qualifier("mvcConversionService") FormattingConversionService conversionService,
      @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
   // Must be @Primary for MvcUriComponentsBuilder to work
   return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
         resourceUrlProvider);
}

RequestMappingHandlerMapping

RequestMappingHandlerMapping 同时实现了 InitializingBean 接口,看它的 afterPropertiesSet 方法:

1
2
3
4
5
6
7
@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
   //省略,一些属性设置

   super.afterPropertiesSet();
}

看它的父类 AbstractHandlerMethodMapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void afterPropertiesSet() {
   initHandlerMethods();
}

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #getCandidateBeanNames()
 * @see #processCandidateBean
 * @see #handlerMethodsInitialized
 */
protected void initHandlerMethods() {
   for (String beanName : getCandidateBeanNames()) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
         processCandidateBean(beanName);
      }
   }
   handlerMethodsInitialized(getHandlerMethods());
}

processCandidateBean 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
   }
}

当 bean isHandler 时,检测该bean内的方法。

isHandler 就是看有没有打了 @Controller@RequestMapping 注解:

1
2
3
4
5
@Override
protected boolean isHandler(Class<?> beanType) {
   return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
         AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

接着看 detectHandlerMethods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protected void detectHandlerMethods(Object handler) {
   Class<?> handlerType = (handler instanceof String ?
         obtainApplicationContext().getType((String) handler) : handler.getClass());

   if (handlerType != null) {
      Class<?> userType = ClassUtils.getUserClass(handlerType);
      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);
               }
            });
      
       //省略
       
       
      methods.forEach((method, mapping) -> {
         Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
         registerHandlerMethod(handler, invocableMethod, mapping);
      });
   }
}

MethodIntrospector.selectMethods 会根据第二个参数返回的metadata(这里是RequestMappingInfo),当metadata不为null时,将该method加入到map<Method,RequestMappingInfo>中返回。getMappingForMethod 方法的主要逻辑就是拿到method上RequestMapping注解的信息:

1
2
3
4
5
6
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);
}

所以最终只会返回打了RequestMapping注解的方法,因为类如GetMapping类的注解其实也是加了@RequestMapping的复合注解。

接下来就进入到了 registerHandlerMethod,注册mapping关系:

1
2
3
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
   this.mappingRegistry.register(mapping, handler, method);
}
1
2
3
4
5
6
public void register(T mapping, Object handler, Method method) {
   // 省略
      this.registry.put(mapping,
            new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
   // 省略
}

registry 就是一个map:

1
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

至此,在 RequestMappingHandlerMapping 中维护了一个RequestMappingInfo和Method关系的map。

请求匹配

springmvc本质就是一个servlet,直接看DIspatcherServlet的doDispatch方法:

1
2
3
4
5
6
7
8
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   // 省略

         // Determine handler for the current request.
         mappedHandler = getHandler(processedRequest);
         
   // 省略
}
1
2
3
4
5
6
7
8
9
10
11
12
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
       // 熟悉的 HandlerMapping 接口
      for (HandlerMapping mapping : this.handlerMappings) {
         HandlerExecutionChain handler = mapping.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}

mapping.getHandler(request) 调用链为:

mapping.getHandler(request) -> AbstractHandlerMaping#getHandler -> AbstractHandlerMethodMapping#getHandlerInternal -> AbstractHandlerMethodMapping#lookupHandlerMethod

lookupHandlerMethod :

1
2
3
4
5
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   // 省略 
   List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
   // 省略
}

又看到熟悉的 mappingRegistry 了。