当前位置:首页 > 后端开发 > 浅探SpringMVC中HandlerExecutionChain之handler、interceptor

浅探SpringMVC中HandlerExecutionChain之handler、interceptor

7个月前 (05-25)53

讲解HandlerExecutionChain之前,先大致了解下SpringMVC的核心开发步骤:

  1. 在web.xml中部署DispaterServlet,并配置springmvc.xml等文件;
  2. 将映射文件请求到处理器HandlerMapping;
  3. HandlerMapping会把请求映射为HandlerExecutionChain类型的handler对象;
  4. 将handler对象作为参数传递给HandlerAdapter的实例化对象,调用其handler方法会生成一个ModelAndView对象;
  5. 通过ViewResolver视图解析器,将上一步骤中生成的ModelAndView解析为View;
  6. DispatcherServlet根据获取到View,将视图返回给用户。

本文分为两个部分进行讲解,第一部分分析Handler, 第二部分分析Interceptor

(一) Handler

首先可以明确HandlerExecutionChain与HanderMapping关系非常紧密,HandlerExecutionChain只能通过HanderMapping接口中的唯一方法来获得,HanderMapping接口定义如下:

package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;

public interface HandlerMapping {
    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
  
    //该方法是HandlerMapping接口中的唯一方法,此方法可以利用用户请求request中的信息来生成HandlerExecutionChain对象
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

  

HandlerMapping架构设计图如下:

浅探SpringMVC中HandlerExecutionChain之handler、interceptor _ Java侠

 可以看到HandlerMapping家族有两个分支,分别是AbstractUrlHandlerMappingAbstractHandlerMethodMapping,它们又统一继承于AbstractHandlerMapping。 


AbstractHandlerMapping是接口HandlerMapping的抽象实现,AbstractHandlerMapping抽象类中实现了部分方法提供给它的子类使用,它还覆了getHandler方法,源码如下:

@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

  // AbstractUrlHandlerMapping和AbstractHandlerMethodMapping均对getHandlerInternal(request)进行了覆写
   Object handler = getHandlerInternal(request);//该方法在本类中有定义,是一个protected型的抽象方法,
                                               //根据给定的request查找handler,如果没有找到handler,则返回一个null
  //如果经过上步没有获取到handler实例,则通过本类中setDefaultHandler(Object defaultHandler)设置默认handler,然后使用getDefaultHandler获得。
  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);
}

 

上面的getHandlerInternal在AbstractUrlHandlerMapping和AbstractHandlerMethodMapping中均有实现:

AbstractUrlHandlerMapping中的getHandlerInternal方法会根据用户请求信息中的URL查找handler:

 /** Look up a handler for the URL path of the given request.  -- 通过匹配URL,将URL与handler联系起来 */
    @Override
    protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
                     ... //
    }

AbstractHandlerMethodMapping中的getHandlerInternal方法则会根据用户request信息中提供的Method来查找handler:

/** Look up a handler method for the given request. -- 普遍用于@requestMaping,匹配内容将它的Method作为handler */
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
         ... //略
    }

 

回到上文中的getHandler方法,它最终返回了一个HandlerExecutionChain,getHandlerExecutionChain(handler, request)方法属于 AbstractHandlerMapping 类的一个受保护类型方法,该方法定义如下:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    // 如果已经是HandlerExecutionChain,则直接使用,否则创建新的
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                //向chain中加入mappedInterceptor类型的拦截器
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            // 向chain中共加入拦截器
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

  

简而言之就是:当用户请求到到DispaterServlet中后,配置的HandlerMapping会根据用户请求(也就是handler)会将它与所有的interceptors封装为HandlerExecutionChain对象,HandlerExecutionChain的作用在源码注释部分简要解释如下:

  Handler execution chain, consisting of handler object and any handler interceptors.

  Returned by HandlerMapping's HandlerMapping.getHandler method.

 

可知通过HandlerMapping实例对象的getHandler方法可以获得一个HandlerExecutionChain对象实例,该实例封装了一个handler处理对象和一些interceptors。HandlerExecutionChain类定义代码不长,其中所有的属性,方法如下:

浅探SpringMVC中HandlerExecutionChain之handler、interceptor _ Java侠 

 

(二)  Interceptor 拦截器

 HandlerExecutionChain中介绍到了拦截器,那这个拦截器是何方圣神?下文来简单介绍一下。

首先引入拦截器。

上文中指出了HandlerMapping继承了AbstractHandlerMapping,AbstractHandlerMapping又继承于WebApplicationObjectSupport,进而继承了ApplicationObjectSupport。

  浅探SpringMVC中HandlerExecutionChain之handler、interceptor _ Java侠

 ApplicationObjectSupport实现了ApplicationContextAware接口,在Spring容器中如果该bean类实现了ApplicationContextAware,那么通过容器获取这个bean时,void setApplicationContext(ApplicationContext context)方法将被调用。

setApplicationContext()方法调用了void initApplicationContext(),该方法在AbstractHandlerMapping中有实现,它主要的作用是加载拦截器以及初始化拦截器的一下配置,源代码如下:
 
@Override  
protected void initApplicationContext() throws BeansException {  
  extendInterceptors(this.interceptors);  
  //探测容器中所有拦截器  
  detectMappedInterceptors(this.mappedInterceptors);  
  //初始配置这些拦截器  
  initInterceptors();  
}  

 其中,initInterceptors()方法如下,主要作用是返回一个HandlerInterceptor对象,然后将这个对象放到 this.adaptedInterceptors 集合中(ps: adaptedInterceptors很重要哟)。

/**
* Initialize the specified interceptors, checking for {@link MappedInterceptor}s and
* adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor}s if necessary.
* @see #setInterceptors
* @see #adaptInterceptor
*/
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));
		}
	}
}

 

至此,了解了拦截器来龙,那接下来说一下拦截器的去脉。

拦截器的总接口 HandlerInterceptor 定义如下:

public interface HandlerInterceptor {
  boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	
  void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;

  void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}

 在spring-webmvc-4.3.1.RELEASE-sources.jar中,HandlerIntercetor共有13个实现类,如MappedInterceptor、webContentInterceptor、ThemeChangeInterceptor、HandlerIntercetorAdapter等,这里就不一一列出来了。

当客户端发送请求后,DispatcherServlet.doDispatch方法中会处理请求,下面贴出doDispatch(req, res)中与拦截器相关的代码:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ...(前面代码略)

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.//获取根据请求获取handler
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.//获取handler适配器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            ...(略)
                    
            // 拦截器执行拦截,对客户端请求响应requset进行拦截
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 核心逻辑,处理handler,返回ModerAndView对象
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
           
            applyDefaultViewName(processedRequest, mv);
            // 拦截器执行拦截,对客户端响应response进行拦截
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            ...(后面代码略)
        }
    }
} 

这里以mappedHandler.applyPreHandle(req, res)为例,简要分析一下doDispatch中的处理请求前拦截逻辑:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 获取所有初始化的拦截器
        HandlerInterceptor[] interceptors = getInterceptors();
        // 遍历拦截器
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                // 遍历执行每个拦截器中的preHandle方法(即对某一个handler,要遍历执行所有的拦截器preHandle方法)
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    // 最终调用interceptor.afterCompletion(req, res, handler, ex)
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    } 

 

当然也可以自定义拦截器,只要实现HandlerInterceptor 接口中的preHandle、postHandle、afterCompletion方法就可以啦。

 

写在最后

其实看源码是最好的,也是最优效果的,本文只希望能在阅读源码的时候提供一点参考作用就够了。

 

作者:有爱jj
来源链接:https://www.cnblogs.com/chenjunjie12321/p/6118426.html

标签: SpringMVC

“浅探SpringMVC中HandlerExecutionChain之handler、interceptor” 的相关文章

SpringMVC作用

SpringMVC作用

SpringMVC负责与用户进行交互 Servlet缺点: 1. 一个Servlet只能完成一个业务处理 2. 每个Servlet的配置信息至...

整合SpringMVC:端口

整合SpringMVC:端口

(1)可以修改tomcat的端口和访问项目中的静态资源 (2)如何修改tomcat端口 查询**Properties se...

一、springMVC、freemarker页面半自动静态化

一、springMVC、freemarker页面半自动静态化

说明:刚刚接到公司的通知,实现(半自动化),即通过参数控制是否需要静态化页面(哪里我说错了,勿喷!谢谢)    1,请求.do的URL时直接生成对应的.htm文件,并将请求转发到该htm...

SpringMVC关于json、xml自动转换的原理研究[附带源码分析]

SpringMVC关于json、xml自动转换的原理研究[附带源码分析]

目录 前言 现象 源码分析 实例讲解 关于配置 总结 参考资料 前言 SpringMVC是目前主流的W...

学习SpringMVC——国际化+上传+下载

学习SpringMVC——国际化+上传+下载

一个软件,一个产品,都是一点点开发并完善起来的,功能越来越多,性能越来越强,用户体验越来越好……这每个指标的提高都需要切切实实的做点东西出来,好比,你的这个产品做大了,用的人多了,不仅仅...

SpringMVC拦截器详解[附带源码分析]

SpringMVC拦截器详解[附带源码分析]

目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 总结 前言 Sprin...

SpringMVC 和Struts2的区别

SpringMVC 和Struts2的区别 1. 机制: spring mvc的入口是servlet,而struts2是filter,这样就...

SpringMVC工作流程

SpringMVC工作流程

    一、SpringMVC简介     SpringMVC是一种基于Spring实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,使用了MVC架...

SpringMVC框架(一)

SpringMVC框架(一)

开篇 我在前面写了Spring,Mybatis的一些知识和用法,开始进入SpringMVC,进入SpringMVC我们就要知道它是哪里来的,是...

SpringMVC + Spring + Mybatis + Maven整合实例

SpringMVC + Spring + Mybatis + Maven整合实例

一、说明: 目前工作中的开发框架主要是 spring ,使用的是 springMVC+ spring + spring JdbcTemplate ,最近学习了一下Mybatis、M...