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的更多相关文章

  1. SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping请求分发

    AbstractHandlerMethodMapping实现接口getHandlerInternal,定义查找流程 RequestMappingInfoHandlerMapping根据RequestM ...

  2. SpringMVC源码解读 - HandlerMapping - AbstractUrlHandlerMapping系列request分发

    AbstractHandlerMapping实现HandlerMapping接口定的getHandler 1. 提供getHandlerInternal模板方法给子类实现 2. 如果没有获取Handl ...

  3. SpringMVC源码解读 - HandlerMapping - SimpleUrlHandlerMapping初始化

    摘要: SimpleUrlHandlerMapping只是参与Handler的注册,请求映射时由AbstractUrlHandlerMapping搞定. 初始化时,通过setMappings(Prop ...

  4. SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping初始化

    RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping来定义controller. @Controller @RequestMapp ...

  5. SpringMVC源码解读 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化

    AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInterna ...

  6. SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系

    一般我们开发时,使用最多的还是@RequestMapping注解方式. @RequestMapping(value = "/", param = "role=guest& ...

  7. SpringMVC源码解读 - RequestMapping注解实现解读

    SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系  https://www.cnblogs.com/leftthen/p/520840 ...

  8. SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo

    使用@RequestMapping注解时,配置的信息最后都设置到了RequestMappingInfo中. RequestMappingInfo封装了PatternsRequestCondition, ...

  9. springMVC源码分析--HandlerMapping(一)

    HandlerMapping的工作就是为每个请求找到合适的请求找到一个处理器handler,其实现机制简单来说就是维持了一个url到Controller关系的Map结构,其提供的实际功能也是根据req ...

随机推荐

  1. 如何ping别人的计算机名来获取IP?

    来源:http://blog.csdn.net/qq_27109081/article/details/47128175 如何ping别人的计算机名来获取IP?            获取别人的IP, ...

  2. Accessing data in Hadoop using dplyr and SQL

    If your primary objective is to query your data in Hadoop to browse, manipulate, and extract it into ...

  3. JAVA面向对象编程课程设计——项目部署

    目录 一.Java环境的安装 1.下载 2.安装 3.配置环境变量 二.Tomcat的安装 1.下载 2.安装 3.启动Tomcat(默认已经安装好java环境,如果未安装java会报错.) 三.My ...

  4. HDU 3179 二叉搜索树(树的建立)

    二叉搜索树 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  5. PDF预览之PDFObject.js总结

    get from:PDF预览之PDFObject.js总结   PDFObject.js - 将PDF嵌入到一个div内,而不是占据整个页面(要求浏览器支持显示PDF,不支持,可配置PDF.js来实现 ...

  6. 551. Student Attendance Record I + Student Attendance Record II

    ▶ 一个学生的考勤状况是一个字符串,其中各字符的含义是:A 缺勤,L 迟到,P 正常.如果一个学生考勤状况中 A 不超过一个,且没有连续两个 L(L 可以有多个,但是不能连续),则称该学生达标(原文表 ...

  7. Sql语句在线转java bean https://www.bejson.com/othertools/sql2pojo/

    https://www.bejson.com/othertools/sql2pojo/

  8. html 1.0 鼠标放上去会亮 onmouseover onmouseout 以及this标签的使用

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Linux TCP/IP 连接查看和问题解决

    netstat -nat|awk '{print awk $NF}'|sort|uniq -c|sort -n  上面的 命令可以帮助分析哪种Tcp状态数量异常     netstat -nat|gr ...

  10. zk分布式锁-排它锁简单实现

    package Lock; import java.util.concurrent.CountDownLatch;import java.util.concurrent.TimeUnit;import ...