glmapper

SpringMVC源码系列:AbstractHandlerMapping

字数统计: 3.3k阅读时长: 13 min
2018/11/10 Share

AbstractHandlerMapping是实现HandlerMapping接口的一个抽象基类。支持排序,默认处理程序,处理程序拦截器,包括由路径模式映射的处理程序拦截器。所有的HandlerMapping都继承自AbstractHandlerMapping。另外,此基类不支持PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE的暴露,此属性的支持取决于具体的子类,通常基于请求URL映射。

前面说到,HandlerMapping的作用就是通过request查找Handler和Interceptors。具体的获取均是通过子类来实现的。

1.AbstractHandlerMapping 的类定义

1
2
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered {

AbstractHandlerMapping继承了WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext;AbstractHandlerMapping的创建也就是在这个方法里面完成的。同时实现了HandlerMapping和Ordered接口,这也就是上面提到的支持排序的原因。

2.AbstractHandlerMapping属性分析

  • 排序值 order

    默认值为Integer的最大值,后面注释的意思是和没有排序是一样的,因为只有理论上才可能超过Integer.MAX_VALUE。

    1
    private int order = Integer.MAX_VALUE;  // default: same as non-Ordered
  • 默认处理器 defaultHandler

    1
    private Object defaultHandler;
  • Spring工具类 urlPathHelper

    Helper类用于URL路径匹配。提供对RequestDispatcher中URL路径的支持,包括并支持一致的URL解码。

    1
    private UrlPathHelper urlPathHelper = new UrlPathHelper();
  • spring工具类 PathMatcher(AntPathMatcher)

    用于基于字符串的路径匹配的策略接口。

    1
    private PathMatcher pathMatcher = new AntPathMatcher();
  • 拦截器列表 interceptors

    用于配置SpringMVC的拦截器,配置方式由两种:

    • 1.注册HandlerMapping时通过属性设置
    • 2.通过子类的extendInterceptors钩子方法进行设置(extendInterceptors方法是在initApplicationContext中调用的)

      interceptors并不会直接使用,二是通过initInterceptors方法按照类型分配到mappedInterceptors和adaptedInterceptors中进行使用,interceptors只用于配置。

      1
      private final List<Object> interceptors = new ArrayList<Object>();
  • adaptedInterceptors

    被分配到adaptedInterceptors中的类型的拦截器不需要进行匹配,在getHandler中会全部添加到返回值HandlerExecutionChain里面。他 只能从 interceptors中获取。

    1
    private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
  • corsProcessor

    CorsProcessor作用是接受请求和CorsConfiguration并更新响应的策略。
    此组件不关心如何选择CorsConfiguration,而是采取后续操作,例如应用CORS验证检查,并拒绝响应或将CORS头添加到响应中。

    1
    private CorsProcessor corsProcessor = new DefaultCorsProcessor();
  • corsConfigSource

    根据路径模式上映射的CorsConfiguration集合提供每个请求的CorsConfiguration实例。支持精确的路径映射URI(如“/ admin”)以及Ant样式的路径模式(如“/ admin / **”)

    1
    2
    private final UrlBasedCorsConfigurationSource corsConfigSource 
    = new UrlBasedCorsConfigurationSource();
  • 跨域相关问题

CorsConfiguration 具体封装跨域配置信息的pojo

CorsConfigurationSource request与跨域配置信息映射的容器

CorsProcessor 具体进行跨域操作的类

3.AbstractHandlerMapping 中的get&set方法

3.1 setOrder

指定此HandlerMapping bean的排序值。

1
2
3
public final void setOrder(int order) {
this.order = order;
}

3.2 setDefaultHandler

指定此HandlerMapping bean的排序值。
设置此处理程序映射的默认处理程序。
如果没有找到特定的映射,这个处理程序将被返回。
缺省值为null,表示没有默认处理程序。

1
2
3
public void setDefaultHandler(Object defaultHandler) {
this.defaultHandler = defaultHandler;
}

3.3 getDefaultHandler

返回此处理程序映射的默认处理程序,如果没有,则返回null。

1
2
3
public Object getDefaultHandler() {
return this.defaultHandler;
}

3.4 setAlwaysUseFullPath

如果URL查找始终使用当前servlet上下文中的完整路径,请进行设置。 否则,如果适用,则使用当前servlet映射中的路径(即,在web.xml中“… / *”servlet映射的情况下)。
默认是“false”。setAlwaysUseFullPath中的实现具体是委托给urlPathHelper和corsConfigSource来完成的。

1
2
3
4
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
this.corsConfigSource.setAlwaysUseFullPath(alwaysUseFullPath);
}

3.5 setUrlDecode

如果上下文路径和请求URI应该被URL解码,则设置。两者都是由Servlet API返回“undecoded”,与servlet路径相反。根据Servlet规范(ISO-8859-1)使用请求编码或默认编码。setUrlDecode中的实现具体是委托给urlPathHelper和corsConfigSource来完成的。

1
2
3
4
public void setUrlDecode(boolean urlDecode) {
this.urlPathHelper.setUrlDecode(urlDecode);
this.corsConfigSource.setUrlDecode(urlDecode);
}

3.6 setRemoveSemicolonContent

如果“;” (分号)内容应该从请求URI中去除,则设置。默认值是true。setRemoveSemicolonContent中的实现具体是委托给urlPathHelper和corsConfigSource来完成的。

1
2
3
4
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
this.corsConfigSource.setRemoveSemicolonContent(removeSemicolonContent);
}

3.7 setUrlPathHelper

设置UrlPathHelper以用于解析查找路径。
使用它可以用自定义子类覆盖默认的UrlPathHelper,或者跨多个HandlerMappings和MethodNameResolvers共享通用的UrlPathHelper设置。

1
2
3
4
5
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
this.urlPathHelper = urlPathHelper;
this.corsConfigSource.setUrlPathHelper(urlPathHelper);
}

3.8 getUrlPathHelper

返回UrlPathHelper实现以用于解析查找路径。

1
2
3
public UrlPathHelper getUrlPathHelper() {
return urlPathHelper;
}

3.9 setPathMatcher

将PathMatcher实现设置为用于匹配注册的URL模式的URL路径。 默认是AntPathMatcher。

1
2
3
4
5
public void setPathMatcher(PathMatcher pathMatcher) {
Assert.notNull(pathMatcher, "PathMatcher must not be null");
this.pathMatcher = pathMatcher;
this.corsConfigSource.setPathMatcher(pathMatcher);
}

3.10 setInterceptors

设置拦截器以应用此处理程序映射映射的所有处理程序。
支持的拦截器类型是HandlerInterceptor,WebRequestInterceptor和MappedInterceptor。
映射拦截器仅适用于请求与其路径模式相匹配的URL。映射的拦截器Bean在初始化期间也会按类型检测到。

1
2
3
ublic void setInterceptors(Object... interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors));
}

其他几个get&set方法就不列出来了,有兴趣的小伙伴可以自行阅读...

4. AbstractHandlerMapping的创建

因为AbstractHandlerMapping继承了WebApplicationObjectSupport类,因此AbstractHandlerMapping的创建就是依托于模板方法initApplicationContext来完成的。

1
2
3
4
5
6
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}

从方法结构可以了解到,initApplicationContext中包括三个子处理方法。

  • extendInterceptors:这也是一个模板方法,在AbstractHandlerMapping中并没有具体实现(方法体是空的),主要是用于给子类提供一个添加(修改)Interceptors的入口(现有的SpringMVC实现中均未使用)。
  • detectMappedInterceptors:用于将SpringMVC容器及父容器中的所有MappedInterceptor类型的Bean添加到MappedInterceptors属性中。

    检测MappedInterceptor类型的bean,并将它们添加到映射的拦截器列表中。 除了可能通过setInterceptors提供的任何MappedInterceptors之外,还会调用此方法,默认情况下将从当前上下文及其祖先中添加所有MappedInterceptor类型的Bean。子类可以覆盖和优化这个策略。

    1
    2
    3
    4
    5
    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    mappedInterceptors.addAll(
    BeanFactoryUtils.beansOfTypeIncludingAncestors(
    getApplicationContext(), MappedInterceptor.class, true, false).values());
    }
  • initInterceptors:初始化指定的拦截器,检查MappedInterceptors并根据需要调整HandlerInterceptors和WebRequestInterceptors。(当前Spring版本时4.3.6)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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");
    }
    this.adaptedInterceptors.add(adaptInterceptor(interceptor));
    }
    }
    }

这个是4.1.5版本的initInterceptors方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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) {
this.mappedInterceptors.add((MappedInterceptor) interceptor);
}
else {
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
}

在4.1.5中版本中,initInterceptors的工作是将interceptors属性里面所包含的对象按照类型添加到adaptedInterceptors或者mappedInterceptors中。在4.1.5版本中mappedInterceptors是AbstractHandlerMapping的属性之一。主要原因是因为,springMVC自4.2开始添加了跨域的支持,也就是上面属性中的后两个。PS:在阅读Spring相关源码时需要关注不同版本的变更及区别,不要只关注某一个版本,另外就是个人觉得阅读源码的关注点应该在编码方式、设计模式使用、设计思想及理念,而不仅仅是知道他是如何实现的】

这里顺便说下mappedInterceptors的作用:mappedInterceptors中的拦截器在使用时需要与请求的url进行匹配,只有匹配成功后才会添加到getHandler的返回值HandlerExecytionChain里。

adaptInterceptor方法:

使给定的拦截器对象适配HandlerInterceptor接口。默认情况下,支持的拦截器类型是HandlerInterceptor和WebRequestInterceptor。每个给定的WebRequestInterceptor将被封装在WebRequestHandlerInterceptorAdapter中。可以在子类中重写。

1
2
3
4
5
6
7
8
9
10
11
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());
}
}

5.Handler和Interceptor的获取

HandlerMapping是通过getHandler方法来获取Handler和Interceptor的。因此在抽象基类AbstractHandlerMapping中提供了具体的实现。并且在AbstractHandlerMapping中,getHandler使用final关键字修饰的,也就是说,子类不能再进行对此方法进行覆盖重写了。

getHandler的作用就是查找给定请求的handler,如果找不到特定请求,则返回到默认handler。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//通过getHandlerInternal方法来获取handler
Object handler = getHandlerInternal(request);
//如果前一个方法没有获取到,则使用默认的handler
if (handler == null) {
//默认的Handler就是AbstractHandlerMapping中的handler属性通过set得到的值
handler = getDefaultHandler();
}
//如果还是没有找到Hander,则直接返回Null
if (handler == null) {
return null;
}
// Bean name or resolved handler?
//如果找到的handler是String类型的,
if (handler instanceof String) {
//则以它为名到spring Mvc的容器中查找相应的Bean
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//先根据handler和request创建一个HandlerExecutionChain对象,
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}

getHandlerInternal

1
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

查找给定请求的handler,如果找不到特定请求,则返回null。 这个方法被getHandler调用; 如果设置了null返回值,将导致默认handler。
在CORS pre-flight请求上,这个方法应该返回一个不匹配飞行前请求的匹配项,而是根据URL路径,“Access-Control-Request-Method”头中的HTTP方法和头文件 从“Access-Control-Request-Headers”头部获得,从而允许CORS配置通过getCorsConfigurations获得,
注意:这个方法也可以返回一个预先构建的HandlerExecutionChain,将一个处理程序对象与动态确定的拦截器组合在一起。状态指定的拦截器将被合并到这个现有的链中。

getHandlerExecutionChain

getLookupPathForRequest:返回给定请求的映射查找路径,如果适用的话,在当前的servlet映射中,或者在web应用程序中返回。如果在RequestDispatcher中调用include请求,则检测包含请求URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//如果handler是HandlerExecutionChain类型则直接强转为HandlerExecutionChain类型,
//如果不是则根据handler创建一个新的HandlerExecutionChain实例对象
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//返回给定请求的映射查找路径
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
//遍历当前adaptedInterceptors链表
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
//如果是MappedInterceptor类型则
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
//拦截器是否应用于给定的请求路径,如果是则返回true
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}

为给定的handler构建一个HandlerExecutionChain,包括可用的拦截器。默认实现用给定的handler,handler映射的通用拦截器以及与当前请求URL相匹配的任何MappedInterceptors构建标准的HandlerExecutionChain。拦截器按照他们注册的顺序添加。为了扩展/重新排列拦截器列表,子类可以覆盖它。

需要注意的是,传入的handler对象可能是原始handler或预构建的HandlerExecutionChain。这个方法应该明确地处理这两种情况,建立一个新的HandlerExecutionChain或者扩展现有的链。为了简单地在自定义子类中添加拦截器,可以考虑调用super.getHandlerExecutionChain(handler,request)并在返回的链对象上调用HandlerExecutionChain#addInterceptor。

getCorsHandlerExecutionChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, CorsConfiguration config) {
//通过请求头的http方法是否options判断是否预请求,
if (CorsUtils.isPreFlightRequest(request)) {
HandlerInterceptor[] interceptors = chain.getInterceptors();
//如果是使用PreFlightRequest替换处理器
chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
}
else {
//如果是普通请求,添加一个拦截器CorsInterceptor。
chain.addInterceptor(new CorsInterceptor(config));
}
return chain;
}

更新HandlerExecutionChain进行与CORS(HTTP访问控制:跨域资源共享)相关的处理。

  • 对于pre-flight请求,默认实现用一个简单的HttpRequestHandler来替换选择的handler,该HttpRequestHandler调用已配置的setCorsProcessor。(将处理器替换为内部类PreFlightHandler)
  • 对于普通的请求,默认实现插入一个HandlerInterceptor,它执行与CORS有关的检查并添加CORS头。(添加CorsInterceptor拦截器)

AbstractHandlerMapping中的两个内部类

这两个内部类就是用来校验request是否cors,并封装对应的Adapter的。

  • PreFlightRequest是CorsProcessor对于HttpRequestHandler的一个适配器。这样HandlerAdapter直接使用HttpRequestHandlerAdapter处理。
  • CorsInterceptor 是CorsProcessor对于HandlerInterceptorAdapter的适配器。

具体的类信息如下:

PreFlightHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private class PreFlightHandler implements HttpRequestHandler, CorsConfigurationSource {

private final CorsConfiguration config;

public PreFlightHandler(CorsConfiguration config) {
this.config = config;
}

@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
corsProcessor.processRequest(this.config, request, response);
}

@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
return this.config;
}
}

CorsInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private class CorsInterceptor extends HandlerInterceptorAdapter implements CorsConfigurationSource {

private final CorsConfiguration config;

public CorsInterceptor(CorsConfiguration config) {
this.config = config;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

return corsProcessor.processRequest(this.config, request, response);
}

@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
return this.config;
}
}

至此AbstractHandlerMapping中的一些源码就结束了,AbstractHandlerMapping为HandlerMapping的功能提供的一些具体的模板描述,但是具体的细节实现还需要从其子类中来慢慢分析。关于这部分中涉及到的如HandlerExecutionChain,cors跨域等问题,后面会根据实际情况另开篇幅来学习。

大家如果有什么意见或者建议可以在下方评论区留言,也可以给我们发邮件(glmapper_2018@163.com)!欢迎小伙伴与我们一起交流,一起成长。

原文作者:GuoLei Song

原文链接:http://www.glmapper.com/2018/11/10/spring-base-webmvc2/

发表日期:November 10th 2018, 2:02:35 pm

更新日期:November 10th 2018, 2:03:39 pm

版权声明:转载请注明出处

CATALOG
  1. 1. 1.AbstractHandlerMapping 的类定义
  2. 2. 2.AbstractHandlerMapping属性分析
  3. 3. 3.AbstractHandlerMapping 中的get&set方法
    1. 3.1. 3.1 setOrder
    2. 3.2. 3.2 setDefaultHandler
    3. 3.3. 3.3 getDefaultHandler
    4. 3.4. 3.4 setAlwaysUseFullPath
    5. 3.5. 3.5 setUrlDecode
    6. 3.6. 3.6 setRemoveSemicolonContent
    7. 3.7. 3.7 setUrlPathHelper
    8. 3.8. 3.8 getUrlPathHelper
    9. 3.9. 3.9 setPathMatcher
    10. 3.10. 3.10 setInterceptors
  4. 4. 4. AbstractHandlerMapping的创建
  5. 5. 5.Handler和Interceptor的获取
  6. 6. AbstractHandlerMapping中的两个内部类
    1. 6.1. PreFlightHandler
    2. 6.2. CorsInterceptor