阅读《Java EE 开发的颠覆者 Spring Boot 实战》时,在 第二部分->第四章 SpringMVC 基础 中介绍到拦截器的使用,这里介绍拦截器的配置可让普通Bean 实现HandlerInterceptor 接口或者继承HandlerInterceptorAdapter 类来实现自定义拦截器.

因为抽象类HandlerInterceptorAdapter 会多提供一个afterConcurrentHandlingStarted 方法,所以我们先根据继承HandlerInterceptorAdapter 的自定义拦截器类进行深入研究.这里我们先将相关的源码看一看.

HandlerInterceptorAdapter

package org.springframework.web.servlet.handler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
    public HandlerInterceptorAdapter() {
    }

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }

    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    }
}

AsyncHandlerInterceptor

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface AsyncHandlerInterceptor extends HandlerInterceptor {
    default void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    }
}

通过以上源码我们可以看出继承HandlerInterceptorAdapter 实际也是通过实现接口HandlerInterceptor 来达到自定义拦截器的目的,他们之间的区别在于通过接口AsyncHandlerInterceptor 进行了功能增强,从而进一步查看异步请求拦截的效果


具体自定义拦截器代码

配置自定义拦截器到Spring

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author hz
 * @date 19-2-6 16:16
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private TestHandlerInterceptorAdapter testHandlerInterceptorAdapter;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(testHandlerInterceptorAdapter).addPathPatterns("/**/*");
    }
}

继承HandlerInterceptorAdapter 的实例

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author hz
 * @date 19-2-6 10:48
 */
public class TestHandlerInterceptorAdapter extends HandlerInterceptorAdapter {
    /**
     * 在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制等处理;
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return super.preHandle(request, response, handler);
    }

    /**
     * 在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView;
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面),可以根据ex是否为null判断是否发生了异常,进行日志记录;
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
    }

    /**
     * 当Controller中有异步请求方法的时候会触发该方法, 异步请求先支持preHandle、然后执行afterConcurrentHandlingStarted, 异步线程完成之后执行会再执行preHandle、postHandle、afterCompletion
     * @param request
     * @param response
     * @param handler
     * @throws Exception
     */
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
    }
}

测试Controller

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author hz
 * @date 19-2-6 10:29
 */
@RestController
@RequestMapping
public class TestController {
    
    @GetMapping("/test")
    public String test(){
        return "Hello World!";
    }
}

为了便于测试,这里我们将TestHandlerInterceptorAdapter 的方法进行修改以达到测试的效果

public class TestHandlerInterceptorAdapter extends HandlerInterceptorAdapter {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
           super.preHandle(request, response, handler);
           System.out.println("TestHandlerInterceptorAdapter::preHandle");
           String uri = request.getRequestURI();
           System.out.println("当前访问uri : " + uri);
   
           if("/test".equals(uri)){
               return true;
           }
           if("/error".equals(uri)){
               return true;
           }
           System.out.println("拦截器执行拦截");
           return false;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
        System.out.println("TestHandlerInterceptorAdapter::postHandle");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
        System.out.println("TestHandlerInterceptorAdapter::afterCompletion");
    }

    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
        System.out.println("TestHandlerInterceptorAdapter::afterConcurrentHandlingStarted");
    }
}

我们先进行访问localhost:8080 以及未注册路由的请求uri 发现它的实际访问是/localhost:8080/error 并且发现该路由已经被注册(org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration.WhitelabelErrorViewConfiguration#defaultErrorView),不过这是后话,有兴趣的朋友可以研究一下

下面我依次访问 localhost:8080/test localhost:8080 localhost:8080/ceshi

\---------------------------
TestHandlerInterceptorAdapter::preHandle
当前访问uri : /test
TestController:test
TestHandlerInterceptorAdapter::postHandle
TestHandlerInterceptorAdapter::afterCompletion
---------------------------
TestHandlerInterceptorAdapter::preHandle
当前访问uri : /error
TestHandlerInterceptorAdapter::postHandle
TestHandlerInterceptorAdapter::afterCompletion
---------------------------
TestHandlerInterceptorAdapter::preHandle
当前访问uri : /ceshi
拦截器执行拦截

我们可以清楚的看到它们运行了那些方法以及运行这些方法的顺序,以此来根据自己的需求进行拦截器的编写.