博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringMVC源码系列:AbstractHandlerMapping
阅读量:7114 次
发布时间:2019-06-28

本文共 13731 字,大约阅读时间需要 45 分钟。

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 interceptors = new ArrayList();复制代码
  • adaptedInterceptors

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

    private final List
    adaptedInterceptors = new ArrayList
    ();复制代码
  • 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来完成的。

@Overrideprotected 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
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。

@Overridepublic 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)!欢迎小伙伴与我们一起交流,一起成长。

转载地址:http://rjmhl.baihongyu.com/

你可能感兴趣的文章
我的友情链接
查看>>
android studio更改module名字
查看>>
pam_listfile.so
查看>>
***
查看>>
如何实现对EX4200的远程管理
查看>>
Spring Java-based容器配置
查看>>
复制一个系统和安装压缩和scim输入法的安装。
查看>>
String,StringBuffer与StringBuilder的区别??
查看>>
druid连接池推荐配置
查看>>
安装zabbix
查看>>
AS3灯笼效果(纯代码)
查看>>
swoole_server进程的分工
查看>>
Linux账户管理的几个文件
查看>>
JavaFX控件关系罗列
查看>>
使用rbac时,You should configure "authManager" component to use database before ...
查看>>
JavaScript之基础-6 JavaScript 分支结构 (if、if-else、else if、switch-case)
查看>>
用户管理命令
查看>>
slowlog分析anemometer平台搭建
查看>>
LVM配置与管理
查看>>
LVS四层 VS Nginx七层反代(负载均衡)
查看>>