Spring Boot第七弹,别再问我拦截器如何配置了!!!

持续原创输出,点击上方蓝字关注我吧

前言

上篇文章讲了Spring Boot的WEB开发基础内容,相信读者朋友们已经有了初步的了解,知道如何写一个接口。

今天这篇文章来介绍一下拦截器在Spring Boot中如何自定义以及配置。

Spring Boot 版本

本文基于的Spring Boot的版本是 2.3.4.RELEASE 

什么是拦截器?

Spring MVC中的拦截器( Interceptor )类似于Servlet中的过滤器( Filter ),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。

如何自定义一个拦截器?

自定义一个拦截器非常简单,只需要实现 HandlerInterceptor 这个接口即可,该接口有三个可以实现的方法,如下:

  1. preHandle() 方法:该方法会在控制器方法前执行,其返回值表示是否知道如何写一个接口。中断后续操作。当其返回值为 true 时,表示继续向下执行;当其返回值为 false 时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。
  2. postHandle() 方法:该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。
  3. afterCompletion() 方法:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。

如何使其在Spring Boot中生效?

其实想要在Spring Boot生效其实很简单,只需要定义一个配置类,实现 WebMvcConfigurer 这个接口,并且实现其中的 addInterceptors() 方法即可,代码演示如下:

                              @Configuration    public    class   WebConfig   implements   WebMvcConfigurer   {    @Autowired    private  XXX xxx;    @Override     public   void   addInterceptors   (InterceptorRegistry registry)   {   //不拦截的uri    final  String[] commonExclude = {}};  registry.addInterceptor(xxx).excludePathPatterns(commonExclude);  }  }                

举个栗子

开发中可能会经常遇到短时间内由于用户的重复点击导致几秒之内重复的请求,可能就是在这几秒之内由于各种问题,比如 网络 , 事务的隔离性 等等问题导致了数据的重复等问题,因此在日常开发中必须规避这类的重复请求操作,今天就用拦截器简单的处理一下这个问题。

思路

在接口执行之前先对指定接口(比如标注某个 注解 的接口)进行判断,如果在指定的时间内(比如 5秒 )已经请求过一次了,则返回重复提交的信息给调用者。

根据什么判断这个接口已经请求了?

根据项目的架构可能判断的条件也是不同的,比如 IP地址 , 用户唯一标识 、 请求参数 、 请求URI 等等其中的某一个或者多个的组合。

这个具体的信息存放在哪里?

由于是 短时间 内甚至是瞬间并且要保证 定时失效 ,肯定不能存在事务性数据库中了,因此常用的几种数据库中只有 Redis 比较合适了。

如何实现?

第一步,先自定义一个注解,可以标注在类或者方法上,如下:

                              @Target  ({ElementType.METHOD, ElementType.TYPE})   @Retention  (RetentionPolicy.RUNTIME)   public   @interface  RepeatSubmit {   /**  * 默认失效时间5秒  */     long   seconds   ()   default  5  ;  }           

第二步,创建一个拦截器, 注入到IOC容器中 ,实现的思路很简单,判断controller的类或者方法上是否标注了 @RepeatSubmit 这个注解,如果标注了,则拦截判断,否则跳过,代码如下:

                              /**  * 重复请求的拦截器  *  @Component  :该注解将其注入到IOC容器中  */    @Component    public    class   RepeatSubmitInterceptor   implements   HandlerInterceptor   {    /**  * Redis的API  */    @Autowired    private  StringRedisTemplate stringRedisTemplate;    /**  * preHandler方法,在controller方法之前执行  *  * 判断条件仅仅是用了uri,实际开发中根据实际情况组合一个唯一识别的条件。  */    @Override     public   boolean   preHandle   (HttpServletRequest request, HttpServletResponse response, Object handler)   throws  Exception  {   if  (handler  instanceof  HandlerMethod){   //只拦截标注了@RepeatSubmit该注解   HandlerMethod method=(HandlerMethod)handler;   //标注在方法上的@RepeatSubmit   RepeatSubmit repeatSubmitByMethod = AnnotationUtils.findAnnotation(method.getMethod(),RepeatSubmit.class);   //标注在controler类上的@RepeatSubmit   RepeatSubmit repeatSubmitByCls = AnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(), RepeatSubmit.class);   //没有限制重复提交,直接跳过    if  (Objects.isNull(repeatSubmitByMethod)&&Objects.isNull(repeatSubmitByCls))   return   true  ;    // todo: 组合判断条件,这里仅仅是演示,实际项目中根据架构组合条件    //请求的URI   String uri = request.getRequestURI();    //存在即返回false,不存在即返回true   Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(uri,  ""  , Objects.nonNull(repeatSubmitByMethod)?repeatSubmitByMethod.seconds():repeatSubmitByCls.seconds(), TimeUnit.SECONDS);    //如果存在,表示已经请求过了,直接抛出异常,由全局异常进行处理返回指定信息    if  (ifAbsent!=  null  &&!ifAbsent)   throw   new  RepeatSubmitException();  }   return   true  ;  }  }                                                

第三步,在Spring Boot中配置这个拦截器,代码如下:

                              @Configuration    public    class   WebConfig   implements   WebMvcConfigurer   {    @Autowired    private  RepeatSubmitInterceptor repeatSubmitInterceptor;    @Override     public   void   addInterceptors   (InterceptorRegistry registry)   {   //不拦截的uri    final  String[] commonExclude = {  "/error"  ,  "/files/**"  };  registry.addInterceptor(repeatSubmitInterceptor).excludePathPatterns(commonExclude);  }  }                

OK,拦截器已经配置完成,只需要在需要拦截的接口上标注 @RepeatSubmit 这个注解即可,如下:

                              @RestController    @RequestMapping  (  "/user"  )   //标注了@RepeatSubmit注解,全部的接口都需要拦截    @RepeatSubmit    public    class   LoginController   {    @RequestMapping  (  "/login"  )    public  String  login   ()   {   return   "login success"  ;  }  }              

此时,请求这个URI: http://localhost:8080/springboot-demo/user/login 在5秒之内只能请求一次。

注意 :标注在方法上的超时时间会覆盖掉类上的时间,因为如下一段代码:

                             Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(uri,  ""  , Objects.nonNull(repeatSubmitByMethod)?repeatSubmitByMethod.seconds():repeatSubmitByCls.seconds(), TimeUnit.SECONDS);    

这段代码的失效时间先取值 repeatSubmitByMethod 中配置的,如果为null,则取值 repeatSubmitByCls 配置的。

总结

至此,拦截器的内容就介绍完了,其实配置起来很简单,没什么重要的内容。

posted @ 2020-10-10 16:28  小琪琪来啦  阅读(175)  评论(0编辑  收藏  举报
欢迎大家来到我的博客:武汉雅思 | 武汉托福 | 新航道 | 雅思培训 | dnfsf | 天龙sf | 热血江湖sf | 天龙sf | dnfsf