SpringBoot中的HandlerInterceptorAdapter

2022-06-12 11:05:16 浏览数 (1)

关于 HandlerInterceptorAdapter 的相关内容。 由于经常使用到这个来实现自己的拦截器,记录一下~

通过查看源码可以看到,HandlerInterceptorAdapter 实现了 AsyncHandlerInterceptor 接口,

AsyncHandlerInterceptor 又继承了 HandlerInterceptor 接口,对 HandlerInterceptor 进行了扩展,增加了 afterConcurrentHandlingStarted 方法

所以我们实现拦截器常用有两种方式:

  • 直接实现 HandlerInterceptor 接口
  • 继承 HandlerInterceptorAdapter 抽象类

源码如下: HandlerInterceptorAdapter :

代码语言:javascript复制
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }

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

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

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

}

AsyncHandlerInterceptor :

代码语言:javascript复制
public interface AsyncHandlerInterceptor extends HandlerInterceptor {

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

}

HandlerInterceptor :

代码语言:javascript复制
public interface HandlerInterceptor {

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }

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

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

}

HandlerInterceptorAdapter 中提供了以下的方法:

  • preHandle:在方法被调用前执行。在该方法中可以做类似校验的功能。如果返回 true,则继续调用下一个拦截器。如果返回 false,则中断执行,也就是说我们想调用的方法 不会被执行,但是你可以修改 response 为你想要的响应。
  • postHandle:在方法执行后调用。
  • afterCompletion:在整个请求处理完毕后进行回调,也就是说视图渲染完毕或者调用方已经拿到响应。
  • afterConcurrentHandlingStarted:这个方法会在 Controller 方法异步执行时开始执行。 当处理程序启动一个异步请求时,它会在DispatcherServlet 不调用的情况下退出postHandle,afterCompletion就像它通常为同步请求所做的那样,因为请求处理的结果(例如 ModelAndView)可能还没有准备好,并且将从另一个线程并发生成。在这种情况下,afterConcurrentHandlingStarted(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object) 被调用,允许实现在将线程释放到 Servlet 容器之前执行诸如清理线程绑定属性之类的任务。

关于 afterConcurrentHandlingStarted 官方文档是这样写的:当处理程序同时执行时,调用而不是postHandle和afterCompletion。实现可以使用提供的请求和响应,但应避免以与处理程序的并发执行冲突的方式修改它们。此方法的典型用途是清理线程局部变量。

代码实现

新建一个类 MyInterceptor 继承 HandlerInterceptorAdapter

代码语言:javascript复制
public class MyInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute("startTime", System.currentTimeMillis());
        System.out.println(">>>>> MyInterceptor preHandle >>>>>>>>>>>>>>>>>>>>>>");
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long startTime = (long) request.getAttribute("startTime");
        System.out.println("MyInterceptor 执行:"   (System.currentTimeMillis() - startTime));
        System.out.println(">>>>> MyInterceptor postHandle >>>>>>>>>>>>>>>>>>>>>>");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        request.removeAttribute("startTime");
        System.out.println(">>>>> MyInterceptor afterCompletion >>>>>>>>>>>>>>>>>>>>>>");
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
        System.out.println(">>>>> MyInterceptor afterConcurrentHandlingStarted >>>>>>>>>>>>>>>>>>>>>>");
    }

}

新建配置类 MyConfig

代码语言:javascript复制
@Configuration
public class MyConfig implements WebMvcConfigurer {

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

控制器测试类 MyController

代码语言:javascript复制
@RestController
public class MyController {

    @GetMapping(value = "hello_world")
    public String helloWorld() {
        System.out.println("hello_world");
        return "hello world!";
    }
}

启动应用进行访问, http://localhost:8080/hello_world

输出结果:

代码语言:javascript复制
>>>>> MyInterceptor preHandle >>>>>>>>>>>>>>>>>>>>>>
hello_world
MyInterceptor 执行:39
>>>>> MyInterceptor postHandle >>>>>>>>>>>>>>>>>>>>>>
>>>>> MyInterceptor afterCompletion >>>>>>>>>>>>>>>>>>>>>>

运行流程如下:

  • 拦截器执行顺序是按照Spring配置文件中定义的顺序而定的。
  • 会先按照顺序执行所有拦截器的preHandle方法,一直遇到return false为止,比如第二个preHandle方法是return false,则第三个以及以后所有拦截器都不会执行。若都是return true,则按顺序加载完preHandle方法。
  • 然后执行主方法(自己的controller接口),若中间抛出异常,则跟return false效果一致,不会继续执行postHandle,只会倒序执行afterCompletion方法。
  • 在主方法执行完业务逻辑(页面还未渲染数据)时,按倒序执行postHandle方法。若第三个拦截器的preHandle方法return false,则会执行第二个和第一个的postHandle方法和afterCompletion(postHandle都执行完才会执行这个,也就是页面渲染完数据后,执行after进行清理工作)方法。(postHandle和afterCompletion都是倒序执行)

HandlerInterceptorAdapter 用来做拦截器还是很方便的,可以用来实现如下应用场景:

  • 日志记录,可以记录请求信息的日志,以便进行信息监控、信息统计等。
  • 权限检查:如登陆检测,进入处理器检测是否登陆,如果没有直接返回到登陆页面。
  • 性能监控:典型的是慢日志。

不过,有时不一定要用拦截器,可以在网关就做校验,这样可能对业务的侵入更少一些,具体还是按照业务场景来定吧~

End.

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可 Links: https://lixj.fun/archives/springboot中的handlerinterceptoradapter

0 人点赞