SpringMVC源码解读 - HandlerMapping
SpringMVC在请求到handler处理器的分发这步是通过HandlerMapping模块解决的.handlerMapping 还处理拦截器.
先看看HandlerMapping的继承树吧
可以大致这样做个分类:
1. 一个接口HandlerMapping,定义一个api: HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
2. 一个基础抽象类:主要是准备上下文环境,提供getHandlerInternal钩子,封装拦截器到HandlerExecutionChain
3. 基于注解@Controller,@RequestMapping的使用
4. 配置文件中直接配置url到 handler的SimpleUrlHandlerMapping
5. 默认实现BeanNameUrlHandlerMapping
6. Controller子类的映射
看看HandlerMapping吧,就一个getHandler api 非常简单.
// HandlerMapping
- package org.springframework.web.servlet;
- public interface HandlerMapping {
- HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
- }
AbstractHandlerMapping就没有这么简单了
先看AbstractHandlerMapping继承的类,实现的接口
- package org.springframework.web.servlet.handler;
- public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
- implements HandlerMapping, Ordered {
- // ...
- }
WebApplicationObjectSupport用于提供上下文ApplicationContext和ServletContext.
还有这边的initApplicationContext方法,在后续经常会使用到.AbstractHandlerMapping就直接覆写了.
父类里还是实现了ApplicationContextAware和ServletContextAware接口,spring概念很统一.
Ordered用于集合排序.
再接着看AbstractHandlerMapping的属性吧
// AbstractHandlerMapping
- // order赋了最大值,优先级是最小的
- private int order = Integer.MAX_VALUE; // default: same as non-Ordered
- // 默认的Handler,这边使用的Obejct,子类实现的时候,使用HandlerMethod,HandlerExecutionChain等
- private Object defaultHandler;
- // url计算的辅助类
- private UrlPathHelper urlPathHelper = new UrlPathHelper();
- // 基于ant进行path匹配,解决如/books/{id}场景
- private PathMatcher pathMatcher = new AntPathMatcher();
- // 拦截器配置:1,HandlerMapping属性设置;2,extendInterceptors设置
- private final List<Object> interceptors = new ArrayList<Object>();
- // 从interceptors中解析得到,直接添加给全部handler
- private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
- // 使用前需要跟url进行匹配,匹配通过才会使用
- private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
看下拦截器的初始化:
// AbstractHandlerMapping
- @Override
- protected void initApplicationContext() throws BeansException {
- extendInterceptors(this.interceptors);
- detectMappedInterceptors(this.mappedInterceptors);
- initInterceptors();
- }
- /**
- * 提供给子类扩展拦截器,可惜都没有使用
- */
- protected void extendInterceptors(List<Object> interceptors) {
- }
- /**
- * 扫描应用下的MappedInterceptor,并添加到mappedInterceptors
- */
- protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
- mappedInterceptors.addAll(
- BeanFactoryUtils.beansOfTypeIncludingAncestors(
- getApplicationContext(),MappedInterceptor.class, true, false).values());
- }
- /**
- * 归集MappedInterceptor,并适配HandlerInterceptor和WebRequestInterceptor
- */
- protected void initInterceptors() {
- if (!this.interceptors.isEmpty()) {
- for (int i = 0; i < this.interceptors.size(); i++) {
- Object interceptor = this.interceptors.get(i);
- if (interceptor == null) {
- throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
- }
- if (interceptor instanceof MappedInterceptor) {
- mappedInterceptors.add((MappedInterceptor) interceptor);
- }
- else {
- adaptedInterceptors.add(adaptInterceptor(interceptor));
- }
- }
- }
- }
- protected HandlerInterceptor adaptInterceptor(Object interceptor) {
- if (interceptor instanceof HandlerInterceptor) {
- return (HandlerInterceptor) interceptor;
- }
- else if (interceptor instanceof WebRequestInterceptor) {
- return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
- }
- else {
- throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
- }
- }
然后是getHandler(HttpServletRequest request)的实现,这边同时预留getHandlerInternal(HttpServletRequest request)给子类实现
// AbstractHandlerMapping
- public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- Object handler = getHandlerInternal(request);
- if (handler == null) {
- handler = getDefaultHandler();
- }
- if (handler == null) {
- return null;
- }
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- return getHandlerExecutionChain(handler, request);
- }
- protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
最后是封装拦截器到HandlerExecutionChain
adaptedInterceptors直接添加
mappedInterceptors需要根据url匹配通过后添加
// AbstractHandlerMapping
- protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
- HandlerExecutionChain chain =
- (handler instanceof HandlerExecutionChain) ?
- (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
- chain.addInterceptors(getAdaptedInterceptors());
- String lookupPath = urlPathHelper.getLookupPathForRequest(request);
- for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
- if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
- chain.addInterceptor(mappedInterceptor.getInterceptor());
- }
- }
- return chain;
- }
Controller子类的映射,这一分支先看类继承
我们来说说,这边每个类主要的职责
1. AbstractHandlerMapping 准备上下文环境;提供getHandlerInternal钩子;封装拦截器到HandlerExecutionChain
2. AbstractUrlHandlerMapping 实现注册handler的方法供子类使用;实现getHandlerInternal,根据子类初始化的配置信息,查找handler
3. AbstractDetectingUrlHandlerMapping 扫描应用下的Object,迭代后提供钩子方法determineUrlsForHandler由子类决定如何过滤
4. AbstractControllerUrlHandlerMapping 实现determineUrlsForHandler,添加过滤排除的handler操作(配置文件配置),预留钩子方法buildUrlsForHandler给子类实现;同时判断controller的子类
5. ControllerBeanNameHandlerMapping 根据bean name生成url
ControllerClassNameHandlerMapping根据class name生成url
从AbstractUrlHandlerMapping开始看吧,这边只是大致看下代码,如果需要仔细分析,请移步<SpringMVC源码解读 - HandlerMapping - AbstractUrlHandlerMapping系列request分发>
handler的注册
- protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { }
- protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { }
handler的查找
- protected Object getHandlerInternal(HttpServletRequest request) throws Exception {}
- // 根据url查找handler
- protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {}
- // 校验handler
- protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {}
- // 封装拦截器到HandlerExecutionChain
- protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
- String pathWithinMapping, Map<String, String> uriTemplateVariables) {}
AbstractDetectingUrlHandlerMapping,这边一样不展开,具体移步<SpringMVC源码解读 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化>
具体做的事情:
1. 通过覆写initApplicationContext,调用detectHandlers扫描Obejct
2. 提供钩子方法determineUrlsForHandler给子类根据handler生成url
3. 调用父类的registerHandler进行注册
- @Override
- public void initApplicationContext() throws ApplicationContextException {
- super.initApplicationContext();
- detectHandlers();
- }
- protected void detectHandlers() throws BeansException {
- // ...
- }
- /**
- * Determine the URLs for the given handler bean.
- * 钩子而已
- */
- protected abstract String[] determineUrlsForHandler(String beanName);
AbstractControllerUrlHandlerMapping,这边一样不展开,具体移步<SpringMVC源码解读 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化>
具体做的事情;
1. 覆写determineUrlsForHandler添加剔除部分类的逻辑,通过配置文件配置的excludedClasses和excludedPackages在这边使用
2. 判断是否controller的子类
3. 预留buildUrlsForHandler给子类生成url
- @Override
- protected String[] determineUrlsForHandler(String beanName) {
- Class beanClass = getApplicationContext().getType(beanName);
- if (isEligibleForMapping(beanName, beanClass)) {
- return buildUrlsForHandler(beanName, beanClass);
- }
- else {
- return null;
- }
- }
- protected boolean isEligibleForMapping(String beanName, Class beanClass) {}
- protected boolean isControllerType(Class beanClass) {}
- protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass);
ControllerBeanNameHandlerMapping和ControllerClassNameHandlerMapping 直接看源码吧,或者移步<SpringMVC源码解读 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化>
配置文件中直接配置url到 handler的SimpleUrlHandlerMapping,就是使用registerHandlers注册配置文档中的handler,直接看代码或者移步<SpringMVC源码解读 - HandlerMapping - SimpleUrlHandlerMapping初始化>吧
BeanNameUrlHandlerMapping 实现determineUrlsForHandler生成url,直接看代码或者移步<SpringMVC源码解读 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化>吧
基于注解@Controller,@RequestMapping的使用
最难吭的骨头
先看类继承吧
说下各个类的职责吧,具体的分析还是移步下面的文章
<SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping初始化>
<SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping请求分发>
1. AbstractHandlerMethodMaping 定义初始化流程,请求时如何映射
初始化:
1.1.1 扫描应用下的Object
1.1.2 预留isHandler钩子方法给子类判断Object是否handler
1.1.3 迭代扫描每一个handler,找出符合要求的方法,这边判断依然是留给子类实现getMappingForMethod
1.1.4 注册查找到的处理器,需要确保一个匹配条件RequestMappingInfo只能映射到一个handler
1.1.5 根据匹配条件获取url,同样的只是定义流程,具体的算法留给子类实现getMappingPathPatterns
请求request分发处理:
1.2.1 直接字符串匹配的方式,查找handler
1.2.2 匹配条件查找,这边具体的算法交由子类处理getMatchingMapping
1.2.3 排序并获取最佳匹配handler,这边的排序方式还是子类处理getMappingConmparator
1.2.4 分别封装匹配到和未匹配到handler的情况
2. RequestMappingInfoHandlerMapping使用RequestMappingInfo实现匹配条件,RequestMappingInfo的初始化留给子类
2.1 根据RequestMappingInfo生成url ->getMappingPathPatterns
2.2 使用匹配条件查找Handler -> getMatchingMapping
2.3 完成比较器算法 -> getMappingComparator
2.4 覆写handleMatch,缓存n多信息到request
注册pattern,最佳匹配的pattern,url中解析出来的参数,url中解析出来的多值参数,mediaType
2.1.5 覆写handlerNoMatch,最后的挣扎,再尝试匹配一次
3. RequestMappingHandlerMapping 根据注解@Controller @RequestMapping生成RequestMappingInfo,并校验isHandler
3.1 覆写afterPropertiesSet,添加文件后缀判断
3.2 实现isHandler,类上有@Controller @RequestMapping其中一个注解就对
3.3 解析注解内容,生产RequestMappingInfo实例
SpringMVC源码解读 - HandlerMapping的更多相关文章
- SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping请求分发
AbstractHandlerMethodMapping实现接口getHandlerInternal,定义查找流程 RequestMappingInfoHandlerMapping根据RequestM ...
- SpringMVC源码解读 - HandlerMapping - AbstractUrlHandlerMapping系列request分发
AbstractHandlerMapping实现HandlerMapping接口定的getHandler 1. 提供getHandlerInternal模板方法给子类实现 2. 如果没有获取Handl ...
- SpringMVC源码解读 - HandlerMapping - SimpleUrlHandlerMapping初始化
摘要: SimpleUrlHandlerMapping只是参与Handler的注册,请求映射时由AbstractUrlHandlerMapping搞定. 初始化时,通过setMappings(Prop ...
- SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping初始化
RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping来定义controller. @Controller @RequestMapp ...
- SpringMVC源码解读 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化
AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInterna ...
- SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系
一般我们开发时,使用最多的还是@RequestMapping注解方式. @RequestMapping(value = "/", param = "role=guest& ...
- SpringMVC源码解读 - RequestMapping注解实现解读
SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系 https://www.cnblogs.com/leftthen/p/520840 ...
- SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo
使用@RequestMapping注解时,配置的信息最后都设置到了RequestMappingInfo中. RequestMappingInfo封装了PatternsRequestCondition, ...
- springMVC源码分析--HandlerMapping(一)
HandlerMapping的工作就是为每个请求找到合适的请求找到一个处理器handler,其实现机制简单来说就是维持了一个url到Controller关系的Map结构,其提供的实际功能也是根据req ...
随机推荐
- 黄聪:VPS配置Filezilla Server支持FTP的Passive被动模式(FTP连接不上怎么办?有详细教程)
Filezilla Server的配置: 1.Filezilla默认的模式是Port模式,不是Passive被动模式.为了解决防火墙后的客户端连接问题,最好是启用Passive模式.要启动被动模式,首 ...
- 内核程序开发 LED灯顺序点亮、顺序熄灭
根据实际考试情况,你要按顺序这么来干. 首先,把老师给的文件1.LED灯顺序点亮.顺序熄灭导入到虚拟机里你喜欢的目录下 进入此目录1.LED灯顺序点亮.顺序熄灭: 看到这三个文件,依次打开背过,老师出 ...
- UE4 的json读写方式
转自:http://blog.csdn.net/cartzhang/article/details/41009343 JSON 的解析有很多开源库.UE4的JSON使用在代码的Public->S ...
- Spring中使用JDBC
Spring中的数据库异常体系 使用JDBC(不使用Spring)的时候,我们需要强制捕获SQLException,否则无法使用JDBC处理任何事情.SQLException表示尝试访问数据库的时候出 ...
- 精《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #8 调度策略
HACK #8 调度策略 本节介绍Linux的调度策略(scheduling policy).Linux调度策略的类别大致可以分为TSS(Time Sharing System,分时系统)和实时系统这 ...
- Ubuntu 中安装 Docker
检查 Device Mapper 是否存在 sch01ar@ubuntu:~$ ls -l /sys/class/misc/device-mapper 安装 Ubuntu 维护的版本 sch01ar@ ...
- springboot 邮件服务
springboot仍然在狂速发展,才五个多月没有关注,现在看官网已经到1.5.3.RELEASE版本了.准备慢慢在写写springboot相关的文章,本篇文章使用springboot最新版本1.5. ...
- 集合-强大的集合工具类:java.util.Collections中未包含的集合工具
任何对JDK集合框架有经验的程序员都熟悉和喜欢java.util.Collections包含的工具方法.Guava沿着这些路线提供了更多的工具方法:适用于所有集合的静态方法.这是Guava最流行和成熟 ...
- Direcshow之视频捕捉<转>
关于视频捕捉(About Video Capture in Dshow) 1. 视频捕捉Graph的构建 一个能够捕捉音频或者视频的graph图都称之为捕捉graph图.捕捉graph图比一般的文件回 ...
- mysql性能优化-慢查询分析、优化索引和配置【转】
一.优化概述 二.查询与索引优化分析 1性能瓶颈定位 Show命令 慢查询日志 explain分析查询 profiling分析查询 2索引及查询优化 三.配置优化 1) max_connec ...