SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping初始化

 
https://www.cnblogs.com/leftthen/p/5208404.html
 

RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping来定义controller.

 1 @Controller
2 @RequestMapping(value = "books")
3 public class BookController {
4
5 @RequestMapping(value = "/{id}")
6 @ResponseBody
7 public String getBook(@PathVariable("id") String id) {
8 // ...
9 return id;
10 }
11 }

初始化时,3个类的大致分工如下:

  AbstractHandlerMethodMapping定义整个算法流程;

  RequestMappingInfoHandlerMapping提供匹配条件RequestMappingInfo的解析处理;

  RequestMappingHandlerMapping根据@RequestMapping注解生成 RequestMappingInfo,同时提供isHandler实现

整个初始化工作由AbstractHandlerMethodMapping的initHandlerMethods主导.

  1. 使用BeanFactoryUtils扫描应用下的Object或者直接从容器中获取Object

  2. 迭代类,分别判断isHandler判断目标类是否Handler

    2.1 RequestMappingHandlerMapping.isHandler根据@Controller或@RequestMapping注解判断(有任意一个)

  3. 对handler解析出所有需要分发的方法detectHandlerMethods

    3.1 获取原始的Class<?>

    3.2 使用HandlerMethodSelector.selectMethods过滤具体handler method,预留getMappingForMethod模板方法给子类

      RequestMappingHandlerMapping.getMappingForMethod根据类,方法上的RequestMapping注解生成匹配条件RequestMappingInfo

    3.3 对过滤到的每个method进行注册registerHandlerMethod

      a, 使用createHandlerMethod封装处理器为HandlerMethod

      b, 判断之前是否已经匹配条件对应的处理器是否冲突(相同的匹配条件只能有一个对应的处理器)

      c, 设置匹配条件到handler method的映射关系

      d, 从匹配条件中解析出url,并注册到urlMap(url到匹配条件的映射),这边由RequestMappingInfoHandlerMapping.getMappingPathPatterns实现

  4. 对HandlerMethod进行初始化handlerMethodsInitialized,其实现在什么都没做

在讲初始化之前,我们先来聊聊使用到的一些概念

  1. 映射关系,url到匹配条件RequestMappingInfo,匹配条件到HandlerMethod

  2. 特殊的MultiValueMap,特别在value是个List

  3. 使用到注解@Controller,@RequestMapping

  4. 封装处理器信息的HandlerMethod

  5. 封装各类匹配条件的RequestMappingInfo(诸如pattern,http method,request parameter等)

  6. RequestCondition记录匹配条件

1. 进行request分发前,需要在初始化时准备好映射关系,这边AbstractHandlerMethodMapping中有两个属性保存了映射关系

// AbstractHandlerMethodMapping

1     // 匹配条件到HandlerMethod的映射
2 private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
3 // url到匹配条件的映射
4 private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();

2. 这边的MultiValueMap其实挺简单,就是map的值是个list

1 public interface MultiValueMap<K, V> extends Map<K, List<V>> {
2 // ...
3 }

3. 我们再来看看这边使用到的两个注解:

// @Controller

 1     // org.springframework.stereotype.Controller
2 @Target({ElementType.TYPE})
3 @Retention(RetentionPolicy.RUNTIME)
4 @Documented
5 @Component
6 public @interface Controller {
7
8 /**
9 * The value may indicate a suggestion for a logical component name,
10 * to be turned into a Spring bean in case of an autodetected component.
11 * @return the suggested component name, if any
12 */
13 String value() default "";
14
15 }

// @RequestMapping

 1     // org.springframework.web.bind.annotation.RequestMapping
2 @Target({ElementType.METHOD, ElementType.TYPE})
3 @Retention(RetentionPolicy.RUNTIME)
4 @Documented
5 @Mapping
6 public @interface RequestMapping {
7
8 /**
9 * url路径,如/myPath/*.do
10 */
11 String[] value() default {};
12
13 /**
14 * HTTP request methods 如:GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
15 */
16 RequestMethod[] method() default {};
17
18 /**
19 * requeset parameter 有3种匹配方式,是否包含某个参数,参数值相等,参数值不等于某个值,如myParam!=myValue
20 */
21 String[] params() default {};
22
23 /**
24 * request的header
25 */
26 String[] headers() default {};
27
28 /**
29 * request的content type
30 */
31 String[] consumes() default {};
32
33 /**
34 * 返回内容的content type
35 */
36 String[] produces() default {};
37
38 }
39 }

4. HandlerMethod封装了处理器相关的全部信息,如类Object,方法Method,BeanFactory,参数MethodParameter[],原始方法Method

// HandlerMethod

 1     // org.springframework.web.method.HandlerMethod
2 private final Object bean;// 因为final不可修改,所以下面每次需要修改信息时,都需要new一个
3
4 private final Method method;
5
6 private final BeanFactory beanFactory;
7
8 private final MethodParameter[] parameters;
9
10 private final Method bridgedMethod;

5. 这边匹配条件的范型只有一个实现,RequestMappingInfo.匹配条件里记录的是RequestCondition子类,用于诸如pattern,http method,request parameter等

// RequestMappingInfo

 1     // javax.servlet.http.HttpServletRequest.RequestMappingInfo
2 public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
3
4 private final PatternsRequestCondition patternsCondition;
5
6 private final RequestMethodsRequestCondition methodsCondition;
7
8 private final ParamsRequestCondition paramsCondition;
9
10 private final HeadersRequestCondition headersCondition;
11
12 private final ConsumesRequestCondition consumesCondition;
13
14 private final ProducesRequestCondition producesCondition;
15
16 private final RequestConditionHolder customConditionHolder;
17 // ...
18
19 }

6. 最后再简单看看RequestCondition ,这边定义了3个方法

 1 package org.springframework.web.servlet.mvc.condition;
2 public interface RequestCondition<T> {
3 /**
4 * 拼接条件
5 */
6 T combine(T other);
7
8 /**
9 * 查找匹配的条件,并返回
10 */
11 T getMatchingCondition(HttpServletRequest request);
12
13 /**
14 * 用于排序
15 */
16 int compareTo(T other, HttpServletRequest request);
17 }

看看继承体系吧,老套路,定义接口,然后模板方法实现主要逻辑,具体算法留给子类实现,还有正事要做,还是后期再细化吧.

正文

整个初始化工作由AbstractHandlerMethodMapping的initHandlerMethods主导.copy一段,省得回去比对看

  1. 使用BeanFactoryUtils扫描应用下的Object或者直接从容器中获取Object

  2. 迭代类,分别判断isHandler判断目标类是否Handler

    2.1 RequestMappingHandlerMapping.isHandler根据@Controller或@RequestMapping注解判断(有任意一个)

  3. 对handler解析出所有需要分发的方法detectHandlerMethods

    3.1 获取原始的Class<?>

    3.2 使用HandlerMethodSelector.selectMethods过滤具体handler method,预留getMappingForMethod模板方法给子类

      RequestMappingHandlerMapping.getMappingForMethod根据类,方法上的RequestMapping注解生成匹配条件RequestMappingInfo

    3.3 对过滤到的每个method进行注册registerHandlerMethod

      a, 使用createHandlerMethod封装处理器为HandlerMethod

      b, 判断之前是否已经匹配条件对应的处理器是否冲突(相同的匹配条件只能有一个对应的处理器)

      c, 设置匹配条件到handler method的映射关系

      d, 从匹配条件中解析出url,并注册到urlMap(url到匹配条件的映射),这边由RequestMappingInfoHandlerMapping.getMappingPathPatterns实现

  4. 对HandlerMethod进行初始化handlerMethodsInitialized,其实现在什么都没做

// AbstractHandlerMethodMapping

 1 /** 这个方法哪来的,具体看备注的InitializingBean
2 * Detects handler methods at initialization.
3 */
4 public void afterPropertiesSet() {
5 initHandlerMethods();
6 }
7
8 /**扫描ApplicationContext中的bean,然后筛选handler method 并注册
9 * Scan beans in the ApplicationContext, detect and register handler methods.
10 * @see #isHandler(Class)
11 * @see #getMappingForMethod(Method, Class)
12 * @see #handlerMethodsInitialized(Map)
13 */
14 protected void initHandlerMethods() {
15 if (logger.isDebugEnabled()) {
16 logger.debug("Looking for request mappings in application context: " + getApplicationContext());
17 }
18
19 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
20 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
21 getApplicationContext().getBeanNamesForType(Object.class));
22
23 for (String beanName : beanNames) {
24 if (isHandler(getApplicationContext().getType(beanName))){
25 detectHandlerMethods(beanName);
26 }
27 }
28 handlerMethodsInitialized(getHandlerMethods());
29 }

预留给子类实现的判断handler,实际是由RequestMappingHandlerMapping实现  

// AbstractHandlerMethodMapping

1     /**
2 * Whether the given type is a handler with handler methods.
3 * @param beanType the type of the bean being checked
4 * @return "true" if this a handler type, "false" otherwise.
5 */
6 protected abstract boolean isHandler(Class<?> beanType);

// RequestMappingHandlerMapping

这边判断的逻辑很简单,类上使用Controller或RequestMapping其中至少一个注解就可以.

1     /**
2 * {@inheritDoc}
3 * Expects a handler to have a type-level @{@link Controller} annotation.
4 */
5 @Override
6 protected boolean isHandler(Class<?> beanType) {
7 return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
8 (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
9 }

// AbstractHandlerMethodMapping

 1     /**
2 * Look for handler methods in a handler.
3 * @param handler the bean name of a handler or a handler instance
4 */
5 protected void detectHandlerMethods(final Object handler) {
6 Class<?> handlerType = (handler instanceof String) ?
7 getApplicationContext().getType((String) handler) : handler.getClass();
8
9 final Class<?> userType = ClassUtils.getUserClass(handlerType);
10
11 Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
12 public boolean matches(Method method) {
13 return getMappingForMethod(method, userType) != null;
14 }
15 });
16
17 for (Method method : methods) {
18 T mapping = getMappingForMethod(method, userType);
19 registerHandlerMethod(handler, method, mapping);
20 }
21 }

// AbstractHandlerMethodMapping

这边具体的实现是由RequestMappingHandlerMapping实现,根据注解生产匹配关系,这边实现类是RequestMappingInfo,就是代码有点多,慢慢看

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

// RequestMappingHandlerMapping

 1     /**
2 * Uses method and type-level @{@link RequestMapping} annotations to create
3 * the RequestMappingInfo.
4 *
5 * @return the created RequestMappingInfo, or {@code null} if the method
6 * does not have a {@code @RequestMapping} annotation.
7 *
8 * @see #getCustomMethodCondition(Method)
9 * @see #getCustomTypeCondition(Class)
10 */
11 @Override
12 protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
13 RequestMappingInfo info = null;
14 // 读取方法上的RequestMapping注解信息
15 RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
16 if (methodAnnotation != null) {
17 // 读取自定义的条件,这边没有使用
18 RequestCondition<?> methodCondition = getCustomMethodCondition(method);
19 // 根据方法上的RequsetMapping注解和自定义条件,生成匹配条件.这边的匹配条件包括http method,request parameter,request header等
20 info = createRequestMappingInfo(methodAnnotation, methodCondition);
21 // 读取类上的RequestMapping注解信息
22 RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
23 if (typeAnnotation != null) {
24 RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
25 // 生成类上的匹配条件,并合并方法上的
26 info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
27 }
28 }
29 return info;
30 }

// RequestMappingHandlerMapping

 1     /**
2 * Created a RequestMappingInfo from a RequestMapping annotation.
3 */
4 private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
5 String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
6 return new RequestMappingInfo(
7 new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
8 this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
9 new RequestMethodsRequestCondition(annotation.method()),
10 new ParamsRequestCondition(annotation.params()),
11 new HeadersRequestCondition(annotation.headers()),
12 new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
13 new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()),
14 customCondition);
15 }
16
17 /**
18 * Resolve placeholder values in the given array of patterns.
19 * @return a new array with updated patterns
20 */
21 protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {
22 if (this.embeddedValueResolver == null) {
23 return patterns;
24 }
25 else {
26 String[] resolvedPatterns = new String[patterns.length];
27 for (int i=0; i < patterns.length; i++) {
28 resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
29 }
30 return resolvedPatterns;
31 }
32 }

// AbstractHandlerMethodMapping

 1 /**
2 * Register a handler method and its unique mapping.
3 * @param handler the bean name of the handler or the handler instance
4 * @param method the method to register
5 * @param mapping the mapping conditions associated with the handler method
6 * @throws IllegalStateException if another method was already registered
7 * under the same mapping
8 */
9 protected void registerHandlerMethod(Object handler, Method method, T mapping) {
10 HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
11 HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
12 if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
13 throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()
14 + "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '"
15 + oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
16 }
17
18 this.handlerMethods.put(mapping, newHandlerMethod);// 匹配条件requestMappingInfo 到处理器HandlerMethod
19 if (logger.isInfoEnabled()) {
20 logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
21 }
22
23 Set<String> patterns = getMappingPathPatterns(mapping);
24 for (String pattern : patterns) {
25 if (!getPathMatcher().isPattern(pattern)) {
26 this.urlMap.add(pattern, mapping);// url到匹配条件RequestMappingInfo
27 }
28 }
29 }

// AbstractHandlerMethodMapping

 1     /**
2 * Create the HandlerMethod instance.
3 * @param handler either a bean name or an actual handler instance
4 * @param method the target method
5 * @return the created HandlerMethod
6 */
7 protected HandlerMethod createHandlerMethod(Object handler, Method method) {
8 HandlerMethod handlerMethod;
9 if (handler instanceof String) {
10 String beanName = (String) handler;
11 handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
12 }
13 else {
14 handlerMethod = new HandlerMethod(handler, method);
15 }
16 return handlerMethod;
17 }

// AbstractHandlerMethodMapping

1     /**
2 * Extract and return the URL paths contained in a mapping.
3 */
4 protected abstract Set<String> getMappingPathPatterns(T mapping);

RequestMappingInfoHandlerMapping会实现这个模板方法

// RequestMappingInfoHandlerMapping

1     /**
2 * Get the URL path patterns associated with this {@link RequestMappingInfo}.
3 */
4 @Override
5 protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
6 return info.getPatternsCondition().getPatterns();
7 }

备注:

1. 这边的afterPropertiesSet是因为实现了InitializingBean接口

// org.springframework.beans.factory.InitializingBean

 1 /**
2 * Interface to be implemented by beans that need to react once all their
3 * properties have been set by a BeanFactory: for example, to perform custom
4 * initialization, or merely to check that all mandatory properties have been set.
5 *
6 * <p>An alternative to implementing InitializingBean is specifying a custom
7 * init-method, for example in an XML bean definition.
8 * For a list of all bean lifecycle methods, see the BeanFactory javadocs.
9 *
10 * @author Rod Johnson
11 * @see BeanNameAware
12 * @see BeanFactoryAware
13 * @see BeanFactory
14 * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
15 * @see org.springframework.context.ApplicationContextAware
16 */
17 public interface InitializingBean {
18
19 /**
20 * Invoked by a BeanFactory after it has set all bean properties supplied
21 * (and satisfied BeanFactoryAware and ApplicationContextAware).
22 * <p>This method allows the bean instance to perform initialization only
23 * possible when all bean properties have been set and to throw an
24 * exception in the event of misconfiguration.
25 * @throws Exception in the event of misconfiguration (such
26 * as failure to set an essential property) or if initialization fails.
27 */
28 void afterPropertiesSet() throws Exception;
29
30 }

SpringMVC 和SpringBoot中的注解是如何起作用的,如何实现的的更多相关文章

  1. SpringBoot 中常用注解@Controller/@RestController/@RequestMapping的区别

    SpringBoot中常用注解@Controller/@RestController/@RequestMapping的区别 @Controller 处理http请求 @Controller //@Re ...

  2. SpringBoot 中常用注解@Controller/@RestController/@RequestMapping介绍

    原文 SpringBoot 中常用注解 @Controller/@RestController/@RequestMapping介绍 @Controller 处理http请求 @Controller / ...

  3. SpringBoot 中常用注解

    本篇博文将介绍几种SpringBoot 中常用注解 其中,各注解的作用为: @PathVaribale 获取url中的数据 @RequestParam 获取请求参数的值 @GetMapping 组合注 ...

  4. SpringBoot 中常用注解@PathVaribale/@RequestParam/@GetMapping介绍

    SpringBoot 中常用注解@PathVaribale/@RequestParam/@GetMapping介绍 本篇博文将介绍几种如何处理url中的参数的注解@PathVaribale/@Requ ...

  5. Springboot中PropertySource注解的使用

    https://blog.csdn.net/qq_30739519/article/list/3 注解 https://blog.csdn.net/qq_30739519/article/detail ...

  6. springmvc以及springboot中的拦截器配置

    拦截器两种实现   如果不同的controller中都需要拦截器,不能使用相同的拦截器,因为拦截器不能跨controller,这个时候只能为不同的controller配置不同的拦截器,每一个拦截器只能 ...

  7. spring----spring中的注解@service等的作用

    service  是有用的相当于 xml配置中得bean  id = service  也可以不指定 不指定相当于 bean id =  com. service.service 就是这个类的全限定名 ...

  8. SpringBoot中常用注解@Controller/@RestController/@RequestMapping的区别

    @Controller 处理http请求 @Controller //@ResponseBody public class HelloController { @RequestMapping(valu ...

  9. springboot中常用注解总结

    1.@RestController(组合注解):标注在类上,等价于@Controller和@Responsebody @Controller:将该类标记为Controller层的类,并且注入到Spri ...

随机推荐

  1. MySQL架构原理之存储引擎InnoDB_Redo Log和BinLog

    Redo Log和Binlog是MySQL日志系统中非常重要的两种机制,有很多相似之处同时也有差别,本文探究两者细节和区别. 一.Redo Log 1.Redo Log介绍 Redo:顾名思义就是重做 ...

  2. Django中ORM创建表关系

    一:django中ORM创建表关系 ORM创建外键关系 1.表与表之间的关系 1.表与表之间的关系 一对多 一对一 多对多 2.操作目标条件: 图书表 出版社表 作者表 作者详情表 3.外键关联 一对 ...

  3. python-xlutils模块-修改excel

    前言 xlrd库用于读取excel文件中的数据,xlwt库用于将数据写入excel文件,修改用xlutils模块: xlutils库也仅仅是通过复制一个副本进行操作后保存一个新文件,像是xlrd库和x ...

  4. OpenGL ES 3D空间中自定义显示空间

    在Android中,我们所熟知的是在ES管线中,其在图元装配时,会进行图元组装与图元分配,这样就回剪裁出来视景体中的物体.但是如果我想在3D场景中规定一个区域,凡是在这个区域中的物体就能显示出来,非这 ...

  5. Maven-compiler-plugin 插件编译过程中报缺失get/set方法

    一般都是由于使用了lombok插件导致的 解决方法: 首先将compiler插件版本升级到3.3以上, 查询资料发现说有可能是版本太低导致 在configuration中添加上annotationPr ...

  6. 不需要高价购买BI工具,掌握这个Excel插件就能碾压大数据

    ​曾几何时,EXCEL在数据界可谓是独树一帜,引领风骚,在职场中无人不知,无人不晓.但是随着大数据的出现,EXCEL便风光不再,江河日下,一度被其他大数据工具挤到了后面,逐渐被边缘化了.而我是一个EX ...

  7. 网络中delay和latency的区别

    时序分析基本概念是Latency, 时钟传播延迟.主要指从Clock源到时序组件Clock输入端的延迟时间.它可以分为两个部分:时钟源插入延迟(source latency)和时钟网络延迟(netwo ...

  8. 截图工具snipaste

    下载地址: https://zh.snipaste.com/download.html 使用: 按F1截图,截图后按F3悬浮

  9. 【译】ASP.NET Core 6 中的性能改进

    原文 | Brennan Conroy 翻译 | 郑子铭 受到 Stephen Toub 关于 .NET 性能的博文的启发,我们正在写一篇类似的文章来强调 6.0 中对 ASP.NET Core 所做 ...

  10. Angular + Github action + Nginx 部署

    在常规 Web 的开发流程之中,当开发步入尾声时就需要部署应用到服务器上,Angular 部署上还算简单.经过测试,部署平均用时在5分钟左右,主要受限于 GitHub 海外服务器的网速. 一.Angu ...