在springboot中,有各类各样的注解,这些注解可以简化咱们的配置,提升开发效率。通常来讲,springboot提供的注解已经佷丰富了,但若是咱们想针对某个特定情景来添加注解,就可使用自定义注解。web
实现这个自定义注解通常主要有如下几个步骤。spring
之因此会想到这个自定义注解,是由于咱们在给用户发送邮件这个模块中,用户若是提交了请求,提交按钮被禁用,这个时候用户若是刷新页面的话,这仍然是一条post请求,然后面的这条请求咱们不该该处理,而是提醒用户已经发送了。缓存
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>21.0</version> </dependency>
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface LocalLock { String key() default ""; int expire() default 5; }
@Aspect @Configuration public class LockMethodInterceptor { private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder() // 最大缓存 100 个 .maximumSize(1000) // 设置写缓存后 60 秒钟过时 .expireAfterWrite(60, TimeUnit.SECONDS) .build(); @Around("execution(public * *(..)) && @annotation(com.buaabetatwo.phyweb.annotation.LocalLock)") public Object interceptor(ProceedingJoinPoint pjp) { myToken = 1; MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); LocalLock localLock = method.getAnnotation(LocalLock.class); String key = getKey(localLock.key(), pjp.getArgs()); if (!StringUtils.isEmpty(key)) { if (CACHES.getIfPresent(key) != null) { myToken = 0; // throw new RuntimeException("请勿重复请求"); } // 若是是第一次请求,就将 key 当前对象压入缓存中 CACHES.put(key, key); } try { return pjp.proceed(); } catch (Throwable throwable) { throw new RuntimeException("服务器异常"); } finally { // CACHES.invalidate(key) } } private String getKey(String keyExpress, Object[] args) { for (int i = 0; i < args.length; i++) { keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString()); } return keyExpress; }
@LocalLock(key = "myToken") // @PostMapping("/reset-email") public String postResetEmail(String email, Model model) { }
通过以上四个步骤,咱们的自定义注解LocalLock就大功告成了,当用户打开密码找回页面,输入邮箱后,60秒内再次刷新页面会被拦截掉,也就是不会出现重复提交表单的状况了。以下图所示。springboot