网站首页 > 技术教程 正文
了解过SpingMVC流程的同学一定听说过handler,那handler到底是什么?百度翻译过来是处理者,很多文章中称之为处理器.那就按照大部分人的说法称呼它为控制器.说到控制器,会不会联想到我们平常写业务代码中的各种controller,也是控制器,是不是一种东西啊,这里可以大胆猜测一下就是一种东西嘛.好,现在通过源码进行验证猜测!
如果直接从源码中按照类文件类型直接搜索Handler是找不到的,根据springMVC的工作流程开始捋(有很多文章说过这里不在重述),最早出现handler是在这个地方:AbstractHandlerMapping.java中getHandler(),源码如下:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取handler,如果获取为空则使用默认handler,如果默认的也没有则返回null
Object handler = getHandlerInternal(request);
// 省略部分代码....
}
测试案例发送实际请求:
@RequestMapping("/test")
@RestController
public class Test {
@GetMapping("/add")
public String add(String a,Personal personal,@RequestParam(name = "genderType") int gender){
int i=0;
return a;
}
}
对应请求来看一下debugg,Object handler = getHandlerInternal(request)中handler具体是什么内容,截图如下:
从中可以看到handler实际上一个HandlerMethod类型对象,里面的属性有请求所在的类信息、请求方法、请求参数等内容。所以从这里可以认为handler相当于是平常业务代码中每个请求对应的的controller类以及方法信息.上面debug截图对应起来更容易理解!
handler是如何获取的
项目启动过程中会有包扫描,与本文相关的扫描内容是将类上标注@RequestMapping的类信息以及方法上带有@RequestMapping的方法信息扫描到容器中(注意符合restful风格的@GetMapping、@PostMapping等标注的方法信息也会扫描进去,下文会有介绍).每个带有以上注解的方法扫描到容器中都会有一个RequestMappingInfo(请求映射信息,实际上就是注解中各项属性和属性值的数据组装信息)。
具体请求过程实际上是从Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>>中根据请求对应的RequestMappingInfo获取对应的MappingRegistration,其中含有handler(实际上是HandlerMethod)。
先说项目启动,扫描@RequestMapping处理核心处理
RequestMappingHandlerMapping.java中getMappingForMethod(),源码如下:
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 根据方法上标注@RequestMapping注解获取RequestMappingInfo
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 根据方法所在类上标注@RequestMapping注解获取RequestMappingInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 将按照类获取的RequestMappingInfo 与方法获取的RequestMappingInfo 进行组装生成新方法级别上的RequestMappingInfo
info = typeInfo.combine(info);
}
// 省略部分代码....
}
return info;
}
根据类信息或是方法信息创建RequestMapping的处理:RequestMappingHandlerMapping.java中createRequestMappingInfo(),源码如下:
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 获取类或是方法上带有@RequestMapping注解的RequestMapping注解信息(@GetMaping、@PostMapping等都是按照@RequestMapping进行解析,此类注解只不过是指定method为对应的GTE或POST请求方式的@RequestMapping)
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
// 获取类或是方法上自定义请求条件,此处都返回null
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
具体组装RequestMappingInfo 逻辑:RequestMappingHandlerMapping.java中createRequestMappingInfo()的重载方法
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
// 此处是读取@RequestMapping注解中各种属性值.比如说请求路径信息path、请求头信息headers、请求方式信息method等信息。
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}
至此项目启动,能扫描到的类中所有带有@RequestMapping注解的方法都有一个RequestMappingInfo创建加载完成。
发送请求获取HandlerMethod 逻辑,AbstractHandlerMethodMapping.java中getHandlerInternal,源码如下:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取请求路径,此处不做展开介绍
String lookupPath = initLookupPath(request);
// 省略部分代码
try {
// 根据请求路径以及请求信息获取HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
// 省略部分代码
}
获取的具体逻辑在AbstractHandlerMethodMapping.java中lookupHandlerMethod,源码如下:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
// 省略部分代码
bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.getHandlerMethod();
}
// 省略部分代码
}
梳理一下上面的逻辑:HandlerMethod由bestMatch.getHandlerMethod()获取,bestMatch由`matches.get(0)获取,matches是由addMatchingMappings(directPathMatches, matches, request)进行封装处理而来.下面就继续看addMatchingMappings方法.
AbstractHandlerMethodMapping.java中addMatchingMappings(),源码如下:
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
}
}
}
从上面可以发现matches中由Match组装而来,查看Match实现发现HandlerMethod是由getHandlerMethod()而来(见Match实现).到这终于搞清楚HandlerMethod的最终出处.
private class Match {
private final T mapping;
private final MappingRegistration<T> registration;
public Match(T mapping, MappingRegistration<T> registration) {
this.mapping = mapping;
this.registration = registration;
}
public HandlerMethod getHandlerMethod() {
return this.registration.getHandlerMethod();
}
// 省略部分代码.....
}
那问题来了,映射注册器中MappingRegistration中如何进行组装的HandlerMethod.不要着急请继续往下看.
分析发现项目启动过程中,容器会将所有''认为''是handler的bean(实际就是带有@Controller或是@RequestMapping的类,至于为什么下面有讲)中的方法对象method与映射对象信息RequestMapping进行做注册匹配.RequestMappingHandlerMapping.java中isHandler,源码如下:
protected boolean isHandler(Class<?> beanType) {
//定义了哪种beanType符合Handler.即类中有@Controller或是@RequestMapping注解
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
注册匹配具体处理:
将handler与对应的requestMapping做对应匹配放到注册器中.
public void register(T mapping, Object handler, Method method) {
// 省略部分代码....
// 按照handler与method对象进行组装HandlerMethod.(两个参数对应测试案例中的类的beanName:test,以及请求方法对象:public java.lang.String com.it.txm.demo.methodReslove.Test.add(java.lang.String,com.it.txm.demo.methodReslove.Personal,int));创建过程就是简单的构造函数创建,不深入分析.
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 省略部分代码....
// 将requestMappingInfo和包含handlerMethod的信息组装到映射MappingRegistry
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
至此handlerMethod的来龙去脉已经梳理完毕!
handler简单总结
handler相当于是平常业务代码中每个请求对应的的controller类以及方法信息.文章第一部分的debug截图对应起来更容易理解!
handler来源分析总结:
1.项目启动过程中将所带有@RequestMapping注解的方法对应创建一个requestMappingInfo对象(存储注解中各属性信息);
2.项目启动过程中,容器会将所有的认为是handler的bean中的每个方法进行匹配处理,期间会创建handlerMethod,将映射信息RequestMappingInfo与MappingRegistration(其中包含handlerMethod)进行组装map处理,前者为key,后者为value.
3.发送请求,解析请求信息,从Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>>中根据RequestMappingInfo获取MappingRegistration信息,在从中获取HandlerMethod.
原创不易,欢迎评论转发!关注我,坚持分享更多java服务端干货内容!
猜你喜欢
- 2025-01-12 Spring Boot RESTful API设计:最佳实践指南
- 2025-01-12 由 Mybatis 源码畅谈软件设计(二):MappedStatement 和 SqlSource
- 2025-01-12 详细介绍一下Spring Boot中如何使用Hive?
- 2025-01-12 OGG同步到Kafka
- 2025-01-12 由 Mybatis 源码畅谈软件设计(五):ResultMap 的循环引用
- 2025-01-12 【从零开始】5. 向量数据库选型与搭建
- 2025-01-12 Spring Boot 项目轻松集成 Redis
- 2025-01-12 How China's Drone Manufacturers Leapfrog From Latecomers to Global Leaders
- 2025-01-12 Spring Boot与MyBatis:简化数据库操作
- 2025-01-12 SpringBoot整合ElasticSearch实现数据存储?
你 发表评论:
欢迎- 05-1613步震撼淘宝大促闪光裂纹破墙立体字PS制作教程
- 05-16AI教程 | 绘制扁平的萌萌哒图标
- 05-160基础学平面设计所需了解的基础常识汇总
- 05-16自学平面设计需要多长时间?十六年职业设计总监告诉你
- 05-16平面设计都要学习哪些内容?
- 05-16李涛PS教程 高手之路PS教程 合成教程 —制作一个小星球
- 05-16Illustrator实例教程:制作炫酷的漩涡效果
- 05-16Illustrator实例教程:利用混合工具制作一朵炫酷的花
- 最近发表
- 标签列表
-
- sd分区 (65)
- raid5数据恢复 (81)
- 地址转换 (73)
- 手机存储卡根目录 (55)
- tcp端口 (74)
- project server (59)
- 双击ctrl (55)
- 鼠标 单击变双击 (67)
- debugview (59)
- 字符动画 (65)
- flushdns (57)
- ps复制快捷键 (57)
- 清除系统垃圾代码 (58)
- web服务器的架设 (67)
- 16进制转换 (69)
- xclient (55)
- ps源文件 (67)
- filezilla server (59)
- 句柄无效 (56)
- word页眉页脚设置 (59)
- ansys实例 (56)
- 6 1 3固件 (59)
- sqlserver2000挂起 (59)
- vm虚拟主机 (55)
- config (61)
本文暂时没有评论,来添加一个吧(●'◡'●)