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 ...
随机推荐
- HighCharts之2D颜色阶梯饼图
HighCharts之2D颜色阶梯饼图 1.实例源码 PieGradient.html: <!DOCTYPE html> <html> <head> <met ...
- 错误代码: 1327 Undeclared variable: p_film_count
1.错误描述 1 queries executed, 0 success, 1 errors, 0 warnings 查询:SELECT FOUND_ROWS() INTO p_film_count ...
- 利用Tomcat部署Web项目报错
1.错误描述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help ...
- 深入理解javascript中的事件循环event-loop
前面的话 本文将详细介绍javascript中的事件循环event-loop 线程 javascript是单线程的语言,也就是说,同一个时间只能做一件事.而这个单线程的特性,与它的用途有关,作为浏览器 ...
- Tea HDU - 5881
Tea is good. Tea is life. Tea is everything. The balance of tea is a journey of pursuing balance of ...
- jxl导入/导出excel(网上的案例)
jxl导入/导出excel 1.jxl导入/导出excel案例,黏贴即可运行 package junit.test; import java.io.File; import java.io.IOExc ...
- Java面试题积累
持续积累中... 1.Java支持的数据类型有哪些?什么是自动拆装箱? 数据类型分为两大种,基本类型和引用类型. 基本类型有8种:byte short int long char float doub ...
- npm包管理器小节一下
淘宝npm镜像cnpm设置 npm install -g cnpm --registry=https://registry.npm.taobao.org 更新npm的版本 npm install np ...
- CTSC 选课
题面(有删减) 题目描述 学校实行学分制.每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分.学校开设了N(N<300)门的选修课程,每个学生可选课程的数量M是给定的.学生选修了这M门 ...
- 【learning】凸包
吐槽 计算几何这种东西qwq一开始真的觉得恶心qwq(主要是总觉得为啥画图那么直观的东西非要写一大堆式子来求qwq真的难受qwq) 但其实静下心来学习的话感觉还是很妙的ovo题目思考起来也十分好玩ov ...