进制人生程序员首页投稿(暂停使用,暂停投稿)

SpringMVC源码系列:AbstractHandlerMap

2018-01-11  本文已影响341人  glmapper_2018

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属性分析

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中包括三个子处理方法。

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    mappedInterceptors.addAll(
            BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    getApplicationContext(), MappedInterceptor.class, true, false).values());
}
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访问控制:跨域资源共享)相关的处理。

AbstractHandlerMapping中的两个内部类

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

具体的类信息如下:

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

上一篇下一篇

猜你喜欢

热点阅读