SpringMVC源码系列:AbstractHandlerMap
AbstractHandlerMapping是实现HandlerMapping接口的一个抽象基类。支持排序,默认处理程序,处理程序拦截器,包括由路径模式映射的处理程序拦截器。所有的HandlerMapping都继承自AbstractHandlerMapping。另外,此基类不支持PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE的暴露,此属性的支持取决于具体的子类,通常基于请求URL映射。
前面说到,HandlerMapping的作用就是通过request查找Handler和Interceptors。具体的获取均是通过子类来实现的。
1.AbstractHandlerMapping 的类定义
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered {
AbstractHandlerMapping继承了WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext;AbstractHandlerMapping的创建也就是在这个方法里面完成的。同时实现了HandlerMapping和Ordered接口,这也就是上面提到的支持排序的原因。
2.AbstractHandlerMapping属性分析
-
排序值 order
默认值为Integer的最大值,后面注释的意思是和没有排序是一样的,因为只有理论上才可能超过Integer.MAX_VALUE。
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
-
默认处理器 defaultHandler
private Object defaultHandler;
-
Spring工具类 urlPathHelper
Helper类用于URL路径匹配。提供对RequestDispatcher中URL路径的支持,包括并支持一致的URL解码。
private UrlPathHelper urlPathHelper = new UrlPathHelper();
-
spring工具类 PathMatcher(AntPathMatcher)
用于基于字符串的路径匹配的策略接口。
private PathMatcher pathMatcher = new AntPathMatcher();
-
拦截器列表 interceptors
用于配置SpringMVC的拦截器,配置方式由两种:
- 1.注册HandlerMapping时通过属性设置
- 2.通过子类的extendInterceptors钩子方法进行设置(extendInterceptors方法是在initApplicationContext中调用的)
interceptors并不会直接使用,二是通过initInterceptors方法按照类型分配到mappedInterceptors和adaptedInterceptors中进行使用,****interceptors只用于配置。****
private final List<Object> interceptors = new ArrayList<Object>();
- 1.注册HandlerMapping时通过属性设置
-
adaptedInterceptors
被分配到adaptedInterceptors中的类型的拦截器不需要进行匹配,在getHandler中会全部添加到返回值HandlerExecutionChain里面。他 只能从 interceptors中获取。
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
-
corsProcessor
CorsProcessor作用是接受请求和CorsConfiguration并更新响应的策略。
此组件不关心如何选择CorsConfiguration,而是采取后续操作,例如应用CORS验证检查,并拒绝响应或将CORS头添加到响应中。private CorsProcessor corsProcessor = new DefaultCorsProcessor();
-
corsConfigSource
根据路径模式上映射的CorsConfiguration集合提供每个请求的CorsConfiguration实例。支持精确的路径映射URI(如“/ admin”)以及Ant样式的路径模式(如“/ admin / **”)
private final UrlBasedCorsConfigurationSource corsConfigSource = new UrlBasedCorsConfigurationSource();
-
跨域相关问题
CorsConfiguration 具体封装跨域配置信息的pojo
CorsConfigurationSource request与跨域配置信息映射的容器
CorsProcessor 具体进行跨域操作的类
3.AbstractHandlerMapping 中的get&set方法
3.1 setOrder
指定此HandlerMapping bean的排序值。
public final void setOrder(int order) {
this.order = order;
}
3.2 setDefaultHandler
指定此HandlerMapping bean的排序值。
设置此处理程序映射的默认处理程序。
如果没有找到特定的映射,这个处理程序将被返回。
缺省值为null,表示没有默认处理程序。
public void setDefaultHandler(Object defaultHandler) {
this.defaultHandler = defaultHandler;
}
3.3 getDefaultHandler
返回此处理程序映射的默认处理程序,如果没有,则返回null。
public Object getDefaultHandler() {
return this.defaultHandler;
}
3.4 setAlwaysUseFullPath
如果URL查找始终使用当前servlet上下文中的完整路径,请进行设置。 否则,如果适用,则使用当前servlet映射中的路径(即,在web.xml中“... / *”servlet映射的情况下)。
默认是“false”。setAlwaysUseFullPath中的实现具体是委托给urlPathHelper和corsConfigSource来完成的。
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来完成的。
public void setUrlDecode(boolean urlDecode) {
this.urlPathHelper.setUrlDecode(urlDecode);
this.corsConfigSource.setUrlDecode(urlDecode);
}
3.6 setRemoveSemicolonContent
如果“;” (分号)内容应该从请求URI中去除,则设置。默认值是true。setRemoveSemicolonContent中的实现具体是委托给urlPathHelper和corsConfigSource来完成的。
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
this.corsConfigSource.setRemoveSemicolonContent(removeSemicolonContent);
}
3.7 setUrlPathHelper
设置UrlPathHelper以用于解析查找路径。
使用它可以用自定义子类覆盖默认的UrlPathHelper,或者跨多个HandlerMappings和MethodNameResolvers共享通用的UrlPathHelper设置。
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
this.urlPathHelper = urlPathHelper;
this.corsConfigSource.setUrlPathHelper(urlPathHelper);
}
3.8 getUrlPathHelper
返回UrlPathHelper实现以用于解析查找路径。
public UrlPathHelper getUrlPathHelper() {
return urlPathHelper;
}
3.9 setPathMatcher
将PathMatcher实现设置为用于匹配注册的URL模式的URL路径。 默认是AntPathMatcher。
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在初始化期间也会按类型检测到。
ublic void setInterceptors(Object... interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors));
}
其他几个get&set方法就不列出来了,有兴趣的小伙伴可以自行阅读...
4. AbstractHandlerMapping的创建
因为AbstractHandlerMapping继承了WebApplicationObjectSupport类,因此AbstractHandlerMapping的创建就是依托于模板方法initApplicationContext来完成的。
@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。子类可以覆盖和优化这个策略。
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), MappedInterceptor.class, true, false).values());
}
- initInterceptors:初始化指定的拦截器,检查MappedInterceptors并根据需要调整HandlerInterceptors和WebRequestInterceptors。(当前Spring版本时4.3.6)
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方法:
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中。可以在子类中重写。
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。
@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:
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。
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:
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
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
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)!欢迎小伙伴与我们一起交流,一起成长。