在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