RequestMappingHandlerMapping:这个handlerMapping是基于注解的
同样,先上类图:

通过类图可以看到,同样是继承父类 AbstractHandlerMapping来进行拦截器的初始化工作,实际上处理自己逻辑的只有下面三个类;
需要注意的是RequestMappingHandlerMapping初始化并不是重写initApplicationContext()方法 ,而是通过实现InitializingBean接口来进行初始工作的。 备注:InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是实现该接口的类,在初始化bean的时候会执行该方法。
来看AbstractHandlerMethodMapping 中关键的代码:
     public void afterPropertiesSet() { //实现了InitializingBean接口的方法,进行初始化的入口。
this.initHandlerMethods();
} protected void initHandlerMethods() {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Looking for request mappings in application context: " + this.getApplicationContext());
}
     //扫描应用下所有Object类
String[] beanNames = this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.getApplicationContext(), Object.class) : this.getApplicationContext().getBeanNamesForType(Object.class);
String[] arr$ = beanNames;
int len$ = beanNames.length; for(int i$ = ; i$ < len$; ++i$) {
String beanName = arr$[i$];
if (this.isHandler(this.getApplicationContext().getType(beanName))) { //ishandler由子类实现,是个钩子方法,让子类实现自己的逻辑
this.detectHandlerMethods(beanName);
}
} this.handlerMethodsInitialized(this.getHandlerMethods());//初始化处理器对象,目前是钩子方法,但是也没有子类实现这个方法
}

isHandler方法是在RequestMappingHandlerMapping中实现的

     protected boolean isHandler(Class<?> beanType) { //非常简单, 就是看这个类有没有Controller或者RequestMapping注解,有一个就行
return AnnotationUtils.findAnnotation(beanType, Controller.class) != null || AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null;
}

回到AbstractHandlerMethodMapping看:

     protected void detectHandlerMethods(Object handler) { //开始注册handler
Class<?> handlerType = handler instanceof String ? this.getApplicationContext().getType((String)handler) : handler.getClass();
final Class<?> userType = ClassUtils.getUserClass(handlerType);
      //这块是获取handelr的所有方法,但是有一个过滤器,就是把有匹配条件的的method获取到
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
public boolean matches(Method method) {
return AbstractHandlerMethodMapping.this.getMappingForMethod(method, userType) != null;//getMappingForMethod钩子方法,子类实现
}
});
Iterator i$ = methods.iterator();
//遍历method 进行注册。
while(i$.hasNext()) {
Method method = (Method)i$.next();
T mapping = this.getMappingForMethod(method, userType);
this.registerHandlerMethod(handler, method, mapping);
} }

来看getMappingForMethod的实现,是在RequestMappingHandlerMapping实现的

     protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null; //获取方法上的RequestMapping注解信息
RequestMapping methodAnnotation = (RequestMapping)AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = this.getCustomMethodCondition(method);
info = this.createRequestMappingInfo(methodAnnotation, methodCondition); 构造匹配条件
         // 获取类上的面RequestHandlerMapping注解信息
RequestMapping typeAnnotation = (RequestMapping)AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = this.getCustomTypeCondition(handlerType);
info = this.createRequestMappingInfo(typeAnnotation, typeCondition).combine(info); 构造匹配条件,同方法的进行合并
}
} return info;
}

备注下;RequestMappingInfo 实际上是匹配条件的一个抽象对象,包含了url,method,param,header...等等

来看注册方法前,先看一下处理器是保存在哪的;

 public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
//这块实际上是两个map保存的,泛型实际上就是RequestMappingInfo,这个就是匹配条件 HanlderMethod是封装了处理器全部信息的封装类
     private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap(); //存的是 key:匹配条件 value: 处理器
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap(); //key: url value: 匹配条件

这块讲一下MultiValueMap

public interface MultiValueMap<K, V> extends Map<K, List<V>> //实际上就是个 value是个list的map
     protected void registerHandlerMethod(Object handler, Method method, T mapping) {
HandlerMethod handlerMethod;
if (handler instanceof String) {
String beanName = (String)handler;
handlerMethod = new HandlerMethod(beanName, this.getApplicationContext(), method);
} else {
handlerMethod = new HandlerMethod(handler, method);
} HandlerMethod oldHandlerMethod = (HandlerMethod)this.handlerMethods.get(mapping);
if (oldHandlerMethod != null && !oldHandlerMethod.equals(handlerMethod)) { //不允许存在一个mapping对应多个handlerMethod
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + handlerMethod.getBean() + "' bean method \n" + handlerMethod + "\nto " + mapping + ": There is already '" + oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
} else {
this.handlerMethods.put(mapping, handlerMethod); //存放第一个映射集合
if (this.logger.isInfoEnabled()) {
this.logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
} Set<String> patterns = this.getMappingPathPatterns(mapping); //获取方法的URL
Iterator i$ = patterns.iterator(); while(i$.hasNext()) {
String pattern = (String)i$.next();
if (!this.getPathMatcher().isPattern(pattern)) { //依次放入第二个映射集合
this.urlMap.add(pattern, mapping);
}
} }
}

到此为止,RequestMappingHandlerMapping就初始化完成了。

疑问:  为什么非注解映射器都是通过重写initApplication方法,而注解映射器是通过实现iniliazingBean接口来初始化,这样的好处是什么?

欢迎探讨

Spring MVC的handlermapping之RequestMappingHandlerMapping初始化的更多相关文章

  1. Spring MVC的handlermapping之SimpleUrlHandlerMapping初始化

    前面信息同BeanNameUrlHandlerMapping,这里不再过多分析,详情请看 :Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化 ...

  2. Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化

    先介绍一下: BeanNameUrlHandlerMapping是基于配置文件的方式; 所有处理器需要在XML文件中,以Bean的形式配置. 缺点:配置繁琐; 如果多个URL对应同一个处理器,那么需要 ...

  3. Spring MVC 梳理 - handlerMapping和handlerAdapter分析

    参考图片 综上所述我们来猜测一下spring mvc 中根据URL找到处理器Controller中相应方法的流程 ①:获取Request的URL ②:从UrlLookup这个map中找到相应的requ ...

  4. spring mvc 自定义Handlermapping

    上次大概写了个可以解决velocity 多视图的东西. 但是实际运用过程中又到处找了些资料看了下.这里 小计下: DispatcherServlet解析过程: ..1..HandlerMapping. ...

  5. Spring MVC的handlermapping之请求分发如何找到正确的Handler(BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping)

    本文讲的是Spring MVC如何找到正确的handler, 前面请求具体怎么进入到下面的方法,不再细说. 大概就是Spring mvc通过servlet拦截请求,实现doService方法,然后进入 ...

  6. spring mvc(4) HandlerMapping

    在前面一节里提到,DispatcherServlet在接收到请求后,通过HandlerMapping找到处理请求对应的Controller(其实处理请求器并不一定是Controller,还可以是Htt ...

  7. Spring MVC:HandlerMapping

    HandlerMapping 的类图 Spring中存在两种类型的handlers.第一种是 handler mappings(处理程序映射).它们的角色定位与前面所描述的功能完全相同.它们尝试将当前 ...

  8. Spring MVC 上下文(ApplicationContext)初始化入口

    Spring 常用上下文容器有哪些 ApplicationContext ClassPathXmlApplicationContext ApplicationContext context = new ...

  9. Spring MVC的handlermapping之请求分发如何找到正确的Handler(RequestMappingHandlerMapping)

    这个思路同样是通过在AbstractHandlerMethodMapping里面来实现getHandlerInternal()实现自己的方法来处理寻找正确的处理器,不懂得请看上一篇. protecte ...

随机推荐

  1. 如何修改WinPE Boot的.wim镜像文件

    1. 使用imagex /apply或imagex /mountrw将WIM镜像文件mount到某个文件夹,假设为d:\tmp\winpe_x86\mount.  例: imagex /mountrw ...

  2. HTML5中的输入框

    HTML5中的输入框 1.双引号 <input type="text"/> 2.单引号 <input type='text'/> 3.无引号 <inp ...

  3. 【BZOJ4569】萌萌哒(并查集,倍增)

    [BZOJ4569]萌萌哒(并查集,倍增) 题面 BZOJ 题意: 有一个长度为\(n\)的数 给定\(m\)个限制条件 每次限制\(l1-r1\)与\(l2-r2\)是相同的 求出方案数 题解 如果 ...

  4. 【BZOJ2333】棘手的操作(左偏树,STL)

    [BZOJ2333]棘手的操作(左偏树,STL) 题面 BZOJ上看把... 题解 正如这题的题号 我只能\(2333\) 神TM棘手的题目... 前面的单点/联通块操作 很显然是一个左偏树+标记 ( ...

  5. [SCOI2010]股票交易

    题目大意: 网址:https://www.luogu.org/problemnew/show/P2569 大意:在接下来的T天中,每天股票有一个买入价格Api与卖出价格Bpi. 同时,每天买入股票数与 ...

  6. LightOJ1370 Bi-shoe and Phi-shoe

    题意 给出一些数字,对于每个数字找到一个欧拉函数值大于等于这个数的数,求找到的所有数的最小和. Solution 线性筛出phi,把询问数组排序搞就行了 # include <bits/stdc ...

  7. Idea工具开发 SpringBoot整合JSP(毕设亲测可用)

    因为,临近毕业了,自己虽然也学了很多框架.但是,都是在别人搭建好的基础上进行项目开发.但是springboot的官方文档上明确指出不提倡使用jsp进行前端开发,但是在校期间只学了jsp作为前端页面.所 ...

  8. 常见递归问题 java

    //最长公共子序列 import java.util.*;public class Main { public static void main(String[] args){ int ans = m ...

  9. 洛谷 P1017 进制转换

    推荐洛谷 题目描述 我们可以用这样的方式来表示一个十进制数: 将每个阿拉伯数字乘以一个以该数字所处位置的(值减1)为指数,以10为底数的幂之和的形式.例如:123可表示为 1*10^2+2*10^1+ ...

  10. 怎么解决VMware“该虚拟机似乎正在使用中”问题

    问题如下: