Spring MVC 拦截器的配置使用及源码简析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jeikerxiao/article/details/88292534

Spring MVC 拦截器的配置使用

Demo 下载 Github

1.实现拦截器

实现 HandlerInterceptor 接口,实现里面的三个方法。

第一个拦截器:

/**
 * Description: 第一个拦截器
 * User: jeikerxiao
 * Date: 2019/3/6 9:33 PM
 */
public class FirstHandlerIntercepter implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(FirstHandlerIntercepter.class);

    /**
     * 执行期:进入Handler方法之前执行
     * 应用:用于身份认证、身份授权
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // false代表拦截,不向下执行
        // true代表通过,为方便调试下一步,下面设置为true
        log.info("first Interceptor preHandle");
        return true;
    }

    /**
     * 执行期:进入Handler方法之后,返回ModelAndView之前
     * 应用:用于向ModelAndView中填充公共数据、指定统一的视图
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("first Interceptor  postHandle");
    }

    /**
     * 执行期:进入Handler完成
     * 应用:用于统一异常处理、统一日志处理
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("first Interceptor afterCompletion");
    }
}

第二个拦截器:

/**
 * Description: 第二个拦截器
 * User: jeikerxiao
 * Date: 2019/3/7 9:09 AM
 */
public class SecondHandlerIntercepter implements HandlerInterceptor {

    private final Logger log = LoggerFactory.getLogger(SecondHandlerIntercepter.class);

    /**
     * 执行期:进入Handler方法之前执行
     * 应用:用于身份认证、身份授权
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // false代表拦截,不向下执行
        // true代表通过,为方便调试下一步,下面设置为true
        log.info("second Interceptor preHandle");
        return true;
    }

    /**
     * 执行期:进入Handler方法之后,返回ModelAndView之前
     * 应用:用于向ModelAndView中填充公共数据、指定统一的视图
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("second Interceptor postHandle");
    }

    /**
     * 执行期:进入Handler完成
     * 应用:用于统一异常处理、统一日志处理
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("second Interceptor afterCompletion");
    }
}


2.配置拦截器

在spring-mvc 的配置文件中增加如下拦截器配置,增加URL路由拦截规则,并匹配指定好对应的拦截器类。

例如,演示使用的是 spring-mvc.xml 配置文件:

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!-- 注意多个拦截器顺序执行 -->
    <mvc:interceptor>
        <!-- 拦截所有URL -->
        <mvc:mapping path="/**"/>
        <bean class="com.jeiker.intercepter.FirstHandlerIntercepter"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <!-- 拦截指定URL -->
        <mvc:mapping path="/user/**"/>
        <bean class="com.jeiker.intercepter.SecondHandlerIntercepter"/>
    </mvc:interceptor>
</mvc:interceptors>

3.测试使用的Controller

@RestController
@RequestMapping("/user")
public class UserController {

    private static final Logger log = LoggerFactory.getLogger(UserController.class);

    @GetMapping("/hello")
    public Map<String, String> hello() {
        log.info("hello");
        return Collections.singletonMap("message", "good");
    }
}

4.运行查看日志输出

接口访问:

GET:http://localhost:8080/user/hello

可以看出日志输出如下:

03-07 10:15:14.614 [DEBUG] [org.springframework.web.servlet.DispatcherServlet: 79] - GET "/user/hello", parameters={}
03-07 10:15:17.369 [DEBUG] [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping:420] - Mapped to public java.util.Map<java.lang.String, java.lang.String> com.jeiker.controller.UserController.hello()
03-07 10:15:20.355 [ INFO] [com.jeiker.intercepter.FirstHandlerIntercepter: 28] - first Interceptor preHandle
03-07 10:15:20.911 [ INFO] [com.jeiker.intercepter.SecondHandlerIntercepter: 28] - second Interceptor preHandle
03-07 10:15:21.427 [ INFO] [com.jeiker.controller.UserController: 26] - hello
03-07 10:15:21.429 [DEBUG] [org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor:267] - Using 'application/json', given [*/*] and supported [application/json, application/*+json]
03-07 10:15:21.429 [DEBUG] [org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor: 79] - Writing [{message=good}]
03-07 10:15:23.570 [ INFO] [com.jeiker.intercepter.SecondHandlerIntercepter: 38] - second Interceptor postHandle
03-07 10:15:24.261 [ INFO] [com.jeiker.intercepter.FirstHandlerIntercepter: 38] - first Interceptor  postHandle
03-07 10:15:27.271 [ INFO] [com.jeiker.intercepter.SecondHandlerIntercepter: 47] - second Interceptor afterCompletion
03-07 10:15:27.924 [ INFO] [com.jeiker.intercepter.FirstHandlerIntercepter: 47] - first Interceptor afterCompletion
03-07 10:15:27.925 [DEBUG] [org.springframework.web.servlet.DispatcherServlet:1123] - Completed 200 OK

由日志可以看出拦截器里的方法执行顺序为:

  1. Interceptor1 - preHandle()
  2. Interceptor2 - preHandle()
  3. UserController - hello()
  4. Interceptor2 - postHandle()
  5. Interceptor1 - postHandle()
  6. Interceptor2 - afterCompletion()
  7. Interceptor1 - afterCompletion()

spring mvc 源码分析拦截器执行顺序分析

1. DispatcherServlet中doDispatch()方法

查看分析DispatcherServlet.classdoDispatch()方法

扫描二维码关注公众号,回复: 6407029 查看本文章
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

		try {
			// 判断是否为文件上传请求
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// Determine handler for the current request.
			// 为当前请求查找Controller对应的方法,如找到示例中的UserController.hello()方法
			mappedHandler = getHandler(processedRequest);
			
			... ...
			
			// Determine handler adapter for the current request.
			// 1.确定对应Handler的执行链,在此处决定HandlerExecutionChain,
			// 也就是决定URL对应要执行的拦截器和Handler
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			// Process last-modified header, if supported by the handler.
			// 处理请求头中的last-modified参数
			String method = request.getMethod();
			boolean isGet = "GET".equals(method);
			if (isGet || "HEAD".equals(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}
			// 2.执行请求对应拦截器的preHandle()方法
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// Actually invoke the handler.
			// 3.实际调用对应Handler并执行,如执行示例中的UserController.hello()方法
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
			applyDefaultViewName(processedRequest, mv);
			// 4.执行请求对应拦截器的 postHandle() 方法
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
	
		... ...
	
		// 处理最后的返回结果
		// 5.里面执行请求对应拦截器的 afterCompletion() 方法
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}
	
	... ...
	
}

// 5.里面执行请求对应拦截器的 afterCompletion() 方法
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
		@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
		@Nullable Exception exception) throws Exception {

	... ...

	if (mappedHandler != null) {
		// 5.执行请求对应拦截器的 afterCompletion() 方法
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

2. 查看HandlerExecutionChain中方法执行顺序

查看HandlerExecutionChain中方法执行顺序,以确定Handler 执行链的顺序。

建议使用debug模式,为每个方法打上断点进行调试跟踪方法的执行。

并结合两个自己注册实现的HandlerInterceptor 和执行的Controller中的hello()方法中的日志位置。

打开HandlerExecutionChain.class类文件:


public class HandlerExecutionChain {

	... ...

	/**
	 * 调用执行应用程序中注册的拦截器的 preHandle() 方法
	 */
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

	/**
	 * 调用执行应用程序中注册的拦截器的 postHandle() 方法
	 */
	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}

	/**
	 * 调用执行应用程序中注册的拦截器的 afterCompletion() 方法
	 */
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = this.interceptorIndex; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				try {
					interceptor.afterCompletion(request, response, this.handler, ex);
				}
				catch (Throwable ex2) {
					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
				}
			}
		}
	}
	
	... ...

}

到此分析Spring VMC的拦截器工作的顺序和部分源码解读就完成了。

猜你喜欢

转载自blog.csdn.net/jeikerxiao/article/details/88292534