概要

  • HandlerInterceptor介绍

  • spring boot自定义拦截器实现

HandlerInterceptor介绍

Spring提供的拦截器Interceptor与Servlet中的Filter不同的是, Interceptor采用AOP的方式在Servlet的service方法执行之前进行拦截, 可以进行更精细的控制

Interceptor中有如下方法:

  • preHandle: 在Controller处理之前调用, 返回false时整个请求结束
  • postHandle: 在Controller调用之后执行, 但它会在DispatcherServlet进行视图的渲染之前执行, 也就是说在这个方法中你可以对ModelAndView进行操作
  • afterCompletion: 在整个请求完成之后执行, 也就是DispatcherServlet已经渲染了视图之后执行; 这个方法的主要作用是用于清理资源的
  • afterConcurrentHandlingStarted: 这个方法是AsyncHandlerInterceptor接口中添加的. 当Controller中有异步请求方法的时候会触发该方法, 异步请求先支持preHandle、然后执行afterConcurrentHandlingStarted, 异步线程完成之后执行会再执行preHandle、postHandle、afterCompletion
    关于最后那个方法, 举个列子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @RestController
    public class ExampleController {
    @RequestMapping("/")
    DeferredResult<String> home() {
    DeferredResult<String> dr = new DeferredResult<String>();
    dr.setResult("成功");
    return dr;
    }
    }
    上面这样的Controller里面有个异步结果, 则拦截器的执行顺序将是: preHandle -> afterConcurrentHandlingStarted -> preHandle -> postHandle -> afterCompletion.

如果把dr.setResult(“成功”); 这句删掉, 将只执行preHandle -> afterConcurrentHandlingStarted

可以认为, afterConcurrentHandlingStarted是返回异步结果时调用(此时异步结果里不需要有数据), 而postHandle必须是返回的结果执行完, 异步结果中有数据了(dr.setResult)才调用

spring boot自定义拦截器实现

  1. 拦截器的编写
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    public class AuthInterceptor extends HandlerInterceptorAdapter {

    /**
    * 前置检查
    */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    if (!handler.getClass().isAssignableFrom(HandlerMethod.class)) {
    return true;
    }
    String ip = request.getRemoteAddr();
    long startTime = System.currentTimeMillis();
    request.setAttribute("requestStartTime", startTime);
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    Method method = handlerMethod.getMethod();
    System.out.println("用户:" + ip + ",访问目标:" + method.getDeclaringClass().getName() + "." + method.getName());
    return true;
    }

    /**
    * Controller调用之后执行
    */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    Method method = handlerMethod.getMethod();
    long startTime = (Long) request.getAttribute("requestStartTime");

    long endTime = System.currentTimeMillis();

    long executeTime = endTime - startTime;

    // log it
    if (executeTime > 1000) {
    System.out.println("[" + method.getDeclaringClass().getName() + "." + method.getName() + "] 执行耗时 : "
    + executeTime + "ms");
    } else {
    System.out.println("[" + method.getDeclaringClass().getSimpleName() + "." + method.getName() + "] 执行耗时 : "
    + executeTime + "ms");
    }

    }
    }
  2. 创建配置类继承WebMvcConfigurerAdapter
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    @Configuration
    public class AuthHandlerAdapter extends WebMvcConfigurerAdapter {
    /**
    * 拦截器
    * 由于项目集成了swagger,这里直接不拦截swagger的相关请求
    */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    //AuthInterceptor就是我们自定义的拦截器
    registry.addInterceptor(new AuthInterceptor())
    .addPathPatterns("/**")
    .excludePathPatterns("/swagger-ui.html")
    .excludePathPatterns("/swagger-resources/**")
    .excludePathPatterns("/v2/api-docs");
    }

    /**
    * 资源处理器
    * swagger会和freemarker的静态资源路径冲突因此需配置swagger的资源处理器
    */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/swagger-ui.html")
    .addResourceLocations("classpath:/META-INF/resources/");
    registry.addResourceHandler("/webjars/**")
    .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    }