Spring MVC的handlermapping之RequestMappingHandlerMapping初始化
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初始化的更多相关文章
- Spring MVC的handlermapping之SimpleUrlHandlerMapping初始化
前面信息同BeanNameUrlHandlerMapping,这里不再过多分析,详情请看 :Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化 ...
- Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化
先介绍一下: BeanNameUrlHandlerMapping是基于配置文件的方式; 所有处理器需要在XML文件中,以Bean的形式配置. 缺点:配置繁琐; 如果多个URL对应同一个处理器,那么需要 ...
- Spring MVC 梳理 - handlerMapping和handlerAdapter分析
参考图片 综上所述我们来猜测一下spring mvc 中根据URL找到处理器Controller中相应方法的流程 ①:获取Request的URL ②:从UrlLookup这个map中找到相应的requ ...
- spring mvc 自定义Handlermapping
上次大概写了个可以解决velocity 多视图的东西. 但是实际运用过程中又到处找了些资料看了下.这里 小计下: DispatcherServlet解析过程: ..1..HandlerMapping. ...
- Spring MVC的handlermapping之请求分发如何找到正确的Handler(BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping)
本文讲的是Spring MVC如何找到正确的handler, 前面请求具体怎么进入到下面的方法,不再细说. 大概就是Spring mvc通过servlet拦截请求,实现doService方法,然后进入 ...
- spring mvc(4) HandlerMapping
在前面一节里提到,DispatcherServlet在接收到请求后,通过HandlerMapping找到处理请求对应的Controller(其实处理请求器并不一定是Controller,还可以是Htt ...
- Spring MVC:HandlerMapping
HandlerMapping 的类图 Spring中存在两种类型的handlers.第一种是 handler mappings(处理程序映射).它们的角色定位与前面所描述的功能完全相同.它们尝试将当前 ...
- Spring MVC 上下文(ApplicationContext)初始化入口
Spring 常用上下文容器有哪些 ApplicationContext ClassPathXmlApplicationContext ApplicationContext context = new ...
- Spring MVC的handlermapping之请求分发如何找到正确的Handler(RequestMappingHandlerMapping)
这个思路同样是通过在AbstractHandlerMethodMapping里面来实现getHandlerInternal()实现自己的方法来处理寻找正确的处理器,不懂得请看上一篇. protecte ...
随机推荐
- Java中的换行符
Java中的换行符 PrintWriter out = response.getWriter(); out.write("\r\n"); Java中的换行符"\r\n&q ...
- JAVA之编码---->CSV在文本下是正常的,用EXCEL打开是乱码的问题
JAVA之编码---->CSV在文本下是正常的,用EXCEL打开是乱码的问题 在JAVA下输出文件流,保存成CSV(用UTF-8)文件,怎么处理用EXCEL下是乱码,但是在记事本等其他软件都是正 ...
- Servlet.service() for Servlet jsp threw exception javax.servlet.ServletException:File "/pageFoo
1.错误描述 Servlet.service() for Servlet jsp threw exception javax.servlet.ServletException:File "/ ...
- Vue项目搭建及原理三
我每次写博客都要先在本地写一遍草稿,所以之前有些发布顺序可能会有一丢丢凌乱 哈哈哈,以后绝对改正,那下面我们就说一下创建及项目目录结构吧 三.创建项目 1.初始化Webpack p.p1 { marg ...
- 如何开发jQuery插件
一:普及JQuery知识 知识1:用JQuery写插件时,最核心的方法有如下两个: $.extend(object) 可以理解为,为JQuery 类添加一个静态方法. $.fn.extend(obje ...
- Good Bye 2017 D. New Year and Arbitrary Arrangement
看了别人的题解 首先这题是一个dp dp[i][j] i是当前有多少个a j是当前有多少个ab子序列 dp[i][j] = dp[i+1][j]*Pa + dp[i][i+j]*Pb; i,j 时加一 ...
- Asp.Net WebApi 调试利器“单元测试”
当我们编辑好一个WebApi应用程序后,需要对该Api接口进行调试,传统的调试办法是在方法内设置断点,然后用PostMan等http工具模拟访问进行查看WebAPI的运行情况,但这种除了效率较低还进行 ...
- 回滚 - 每天5分钟玩转 Docker 容器技术(141)
kubectl apply 每次更新应用时 Kubernetes 都会记录下当前的配置,保存为一个 revision(版次),这样就可以回滚到某个特定 revision. 默认配置下,Kubernet ...
- Notepad++ JSON关键字自动提示
Notepad++关键字自动提示 2017-08-31 目录 1 插件安装2 往JSON中加关键字3 智能提示 最近接口测试自动化框架的的case是json格式,用例的json的格式是固定的,定义专门 ...
- Android 运行时权限及APP适配
Android 6.0起,Android加强了权限管理,引入运行时权限概念.对于: 1. Android 5.1(API 22)及以前版本,应用权限必须声明在AndroidManifest.xml中, ...