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

目录

  • 前言
  • Spring Boot 版本
  • 什么是拦截器?
  • 如何自定义一个拦截器?
  • 如何使其在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配置的。

总结

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

上述例子中的源代码有需要的朋友公号回复关键词拦截器即可获取。

Spring Boot 第六弹,拦截器如何配置,看这儿~的更多相关文章

  1. Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证

    本文通过一个简易安全认证示例的开发实践,理解过滤器和拦截器的工作原理. 很多文章都将过滤器(Filter).拦截器(Interceptor)和监听器(Listener)这三者和Spring关联起来讲解 ...

  2. Springboot 系列(六)Spring Boot web 开发之拦截器和三大组件

    1. 拦截器 Springboot 中的 Interceptor 拦截器也就是 mvc 中的拦截器,只是省去了 xml 配置部分.并没有本质的不同,都是通过实现 HandlerInterceptor ...

  3. spring boot 学习(十二)拦截器实现IP黑名单

    拦截器实现IP黑名单 前言 最近一直在搞 Hexo+GithubPage 搭建个人博客,所以没怎么进行 SpringBoot 的学习.所以今天就将上次的”?秒防刷新”进行了一番修改.上次是采用注解加拦 ...

  4. Spring Boot实践——三种拦截器的创建

    引用:https://blog.csdn.net/hongxingxiaonan/article/details/48090075 Spring中的拦截器 在web开发中,拦截器是经常用到的功能.它可 ...

  5. 【spring boot】在自定义拦截器中从request中获取json字符串

    又这样的需求,需要在自定义的拦截器中获取request中的数据,想获取到的是JSON字符串 那需要在拦截器中写这样一个方法 public static String getOpenApiRequest ...

  6. 【Spring】14、SpringMVC拦截器的配置

    拦截器: com.zk.interceptors.MyInterceptor 实现了 HandlerInterceptor接口,可以拦截@RequestMapping注解的类和方法 第一种方式 < ...

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

    持续原创输出,点击上方蓝字关注我吧 前言 上篇文章讲了Spring Boot的WEB开发基础内容,相信读者朋友们已经有了初步的了解,知道如何写一个接口. 今天这篇文章来介绍一下拦截器在Spring B ...

  8. spring boot / cloud (六) 开启CORS跨域访问

    spring boot / cloud (六) 开启CORS跨域访问 前言 什么是CORS? Cross-origin resource sharing(跨域资源共享),是一个W3C标准,它允许你向一 ...

  9. Spring Boot入门(六):使用MyBatis访问MySql数据库(注解方式)

    本系列博客记录自己学习Spring Boot的历程,如帮助到你,不胜荣幸,如有错误,欢迎指正! 本篇博客我们讲解下在Spring Boot中使用MyBatis访问MySql数据库的简单用法. 1.前期 ...

随机推荐

  1. ES5和ES6的继承对比

    ES5的继承实现,这里以最佳实践:寄生组合式继承方式来实现.(为什么是最佳实践,前面有随笔讲过了,可以参考) function Super(name) { this.name = name; } Su ...

  2. mac下使用命令行安装、卸载ipa包、查看日志

    mac下使用命令行安装.卸载ipa包.查看日志 https://www.cnblogs.com/lily1989/p/8383916.html

  3. iOS打电话功能的简单实现

    小功能简介 iOS中的很多小功能都是非常简单的,几行代码就搞定了,比如打电话.打开网址.发邮件.发短信等 打电话-方法3 创建一个UIWebView来加载URL,拨完后能自动回到原应用 if (_we ...

  4. 1.异常页面: /File/ApplyFileForm.aspx 2.异常信息: 基类包括字段“PageOfficeCtrl1”,但其类型(PageOffice.PageOfficeCtrl)与控件(PageOffice.PageOfficeCtrl)的类型不兼容。

    出现页面报错的解决如下: 1.  在  .aspx  页面报错的字段  ID 名称更改: 2.  在没有重复 ID控件,先实行步骤1, 生成项目是ok的,然后,还原,原来的ID名称,再去浏览页面运行, ...

  5. axure rp extension for chrome怎么用

    1)打开文件(原型图)的resources文件夹>chrome文件夹>将文件axure-chrome-extension.crx拉至谷歌浏览器的扩展程序页面,点击确定添加 2)提示出错时, ...

  6. PicGo软件搭配gitee实现图床

    1.安装PicGo软件,并配置gitee 1.1安装picGo picGo 安装gitee-uploader 插件 官网下载地址如下:最新版本 可以自行选择版本进行下载,这里我选择了最新的版本进行下载 ...

  7. 《Offer一箩筐》一份高质量「简历」撰写指南,望打扰!!

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」. 如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! Hi~ 这里是 ...

  8. python 入门,最基础语法集合100行!!

    月开始一直咸到现在,博客难产 心里特别特别愧疚,如此懈怠,怎么对的起我那六个粉丝呢!!!他们一定正日夜翘首以盼,等着我更新博客呢.于是我赶紧到盘里找找看以前的存货 不好意思,拿错了,是这个 我pyth ...

  9. [06] 优化C#服务器的思路和工具的使用

    优化C#服务器的思路和工具的使用 优化服务器之前, 需要先对问题的规模做合理的预估, 然后对关键的数据做采样, 做对比, 看和自己的预估是否一致, 误差大在什么地方, 是预估的不对, 还是系统实现有问 ...

  10. command三国杀开发日记20200914

    目前状态 一时脑热开始写的东西,计划完全使用C语言实现,尽量使用通用接口,能够在windows上直接运行 几乎是一穷二白,初步搭建了牌堆.玩家信息接口体,编写了简单的UI函数,能够将玩家信息显示在屏幕 ...