摘要:實現邏輯在請求調用層時,映射到的方法上加上注解如自定義注解防止多次提交。針對第二個問題解決方法當然是利用代理實現,此處利用的是的動態代理。同時利用開放的拓展處理的接口在實例化后,實例化代理。
前言
此文檔只粗略的講解實現思路,具體的實現邏輯還需要針對業務區別處理。
需求因為此業務中有讀和寫的操作,寫的執行條件依賴于讀,并發條件下可能出現讀到相同的條件均可以去執行寫操作,此時寫就會出現臟數據,。所以項目需要實現,在處理業務時,加鎖防止并發問題,此處利用Redis實現,但是如果多個業務都需要這么操作的話,其實操作Redis的代碼是相同的,這樣就顯得麻煩,所以樓主采用注解的形式實現,具體方法見下述。
實現邏輯在請求調用Controller層時,RequestMapping 映射到的方法上加上注解,如自定義注解 @Debounce(防止多次提交)。
此時需要考慮幾個問題1、利用Redis實現并發鎖的操作對Redis來說實際上就是一種Key的操作,那么自定義注解@Debounce如何實現key的自定義且根據參數可變化?
2、如何實現調用請求真實的處理方法時的攔截?
3、什么情況下才會去做這個事情?
利用處理請求的方法中的參數,實現動態定義,此時又有個問題,就是說如果時基本數據類型+String,這樣的可以直接將值獲取拼接,但是如果參數中有對象的話,同時又想用對象中的屬性作為key值的一部分,那么直接拼接就行不通。像這種情況,統一的方式行不通,那么自然而然就會想到此處必須用到了拓展類,在上層只是定義這種功能,具體的實現由子類負責具體實現。(詳見后述)。
在@Debounce注解中有定義一個處理參數數組,值為處理請求的方法中的參數位置Num,從0開始依次遞增,同時也有個處理類class,作用是具體實現key值的拼接。
當然是利用代理實現,此處利用的是Spring的Cglib動態代理。同時利用Spring開放的拓展Bean處理的接口BeanPostProcessor,在bean實例化后,實例化Cglib代理。
針對第三個問題解決方法在Controller層即在有注解@Controller 或者 @RestController 的類中才會去判斷是否需要做此操作。
具體實現方法 Debounce 注解@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Debounce { /** * 使用的鎖鍵值 */ String lockKey() default ""; /** * 使用方法的參數(toString)做為鎖的KEY使用 * 規則:從0開始計,0表示方法中的第一個參數,以此類推 * 和 lockKey 都為空時,使用方法名進行鎖定 */ int[] methodParameters() default {}; /** * 對注釋中的參數進行修改,默認為字符串拼接 * * @return */ Class extends MethodParametersHandler> handler() default MethodParametersHandler.class; /** * 延時關閉,當調用的方法結束后并不關閉鎖,只有真正超時后才關閉 * 即在鎖定時間內,只允許被調用一次 * * @return */ boolean delayClose() default false; /** * 默認的超時時間,這個時間內除非原來的方法調用結束,否則無法點擊 * 如果原來的方法已經結束,時間比這個短,那么這時間無效 */ @AliasFor("lockExpireEsc") int value() default 5; /** * 鎖的超時時間 * * @return */ @AliasFor("value") int lockExpireEsc() default 5; }參數處理接口 MethodParametersHandler
此處做參數參數值的拼接同時返回拼接后的數據
public interface MethodParametersHandler { String handler(Object[] args) throws IllegalAccessException; static class Default implements MethodParametersHandler { @Override public String handler(Object[] args) { StringBuilder sb = new StringBuilder(); for (Object arg : args) { if (arg != null) { sb.append(String.valueOf(arg)); sb.append("#"); } } return sb.toString(); } } }方法攔截器定義 DebounceInvocationHandler
public class DebounceInvocationHandler implements MethodInterceptor { private MapBean實例化后的實現方式-BeanPostProcessor, MethodParametersHandler> methodParametersHandlerMap = new ConcurrentHashMap<>(); private final Object target; private static final MethodParametersHandler methodParametersHandler = new MethodParametersHandler.Default(); public DebounceInvocationHandler(Object bean) { this.target = bean; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Debounce annotation = method.getAnnotation(Debounce.class); if (annotation != null) { int value = (int) AnnotationUtils.getValue(annotation); if (value <= 0) { value = 10; } // 組裝Redis的key String key = annotation.lockKey(); int[] methodParameters = annotation.methodParameters(); if (methodParameters != null && methodParameters.length > 0) { Object[] handlerArgs = new Object[methodParameters.length]; for (int i = 0; i < methodParameters.length; i++) { if (methodParameters[i] < args.length) { handlerArgs[i] = args[methodParameters[i]]; } } MethodParametersHandler parametersHandler = null; Class extends MethodParametersHandler> handler = annotation.handler(); if (handler == MethodParametersHandler.class) { parametersHandler = methodParametersHandler; } else { if (methodParametersHandlerMap.containsKey(handler)) { parametersHandler = methodParametersHandlerMap.get(handler); } else { MethodParametersHandler instance = handler.newInstance(); parametersHandler = methodParametersHandlerMap.putIfAbsent(handler, instance); } } key += parametersHandler.handler(handlerArgs); } if (StringUtils.isEmpty(key)) { key = method.toString(); } // Redis 的分布式鎖實現,代碼省略 , 不滿足鎖的條件可以直接返回或是拋異常 } try { if (target == null) { return methodProxy.invokeSuper(proxy, args); } else { return methodProxy.invoke(target, args); } } finally { // 釋放Reids 鎖判斷 if (annotation != null && (Redis 鎖不為空) && !annotation.delayClose()) { // 釋放Redis鎖 } } } }
@Configuration public class RestfulMVCAutoConfiguration implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class> beanClass = bean.getClass(); RestController annotation = beanClass.getAnnotation(RestController.class); if (annotation == null) { return bean; } boolean haveDebounce = false; Method[] methods = beanClass.getDeclaredMethods(); for (Method method : methods) { Debounce debounce = method.getAnnotation(Debounce.class); if (debounce != null) { haveDebounce = true; break; } } if (haveDebounce) { Enhancer en = new Enhancer(); en.setSuperclass(beanClass); en.setUseFactory(false); en.setCallback(new DebounceInvocationHandler(bean)); return en.create(); } return bean; } }使用方式
其中的MyHandler.class 為 implements MethodParametersHandler ,參數組裝的具體實現
@RestController @RequestMapping("/test/debounce/") public class DebounceController { @PostMapping(value = "/post") @Debounce(value = 10, handler = MyHandler.class , delayClose = true, methodParameters = 0) public TResponseObject post(@RequestBody MyRequest request) { return TResponseObject.Success("Success"); } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76537.html
摘要:數據結構和算法樹快速排序,堆排序,插入排序其實八大排序算法都應該了解一致性算法,一致性算法的應用的內存結構。如何存儲一個的。八大排序算法一定要手敲一遍快排,堆排尤其重要。面試是一個雙向選擇的過程,不要抱著畏懼的心態去面試,不利于自己的發揮。 前言 16年畢業到現在也近兩年了,最近面試了阿里集團(菜鳥網絡,螞蟻金服),網易,滴滴,點我達,最終收到點我達,網易offer,螞蟻金服二面掛掉,...
摘要:中四種修飾符的限制范圍。數據結構和算法樹快速排序,堆排序,插入排序其實八大排序算法都應該了解一致性算法,一致性算法的應用的內存結構。的部署方式,主從,集群。八大排序算法一定要手敲一遍快排,堆排尤其重要。 前言 15年畢業到現在也近三年了,最近面試了阿里集團(菜鳥網絡,螞蟻金服),網易,滴滴,點我達,最終收到點我達,網易offer,螞蟻金服二面掛掉,菜鳥網絡一個月了還在流程中...最終有...
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數據結構與算法 應用模塊: 常用工具集 ...
摘要:的簡稱,運行環境,為的運行提供了所需環境。分割字符串,返回一個分割后的字符串數組。線程安全是線程安全的,而是非線程安全的。迭代器取代了集合框架中的,迭代器允許調用者在迭代過程中移除元素。 本文分為十九個模塊,分別是:?Java 基礎、容器、多線程、反射、對象拷貝、Java Web 、異常、網絡、設計模式、Spring/Spring MVC、Spring Boot/Spring Clou...
閱讀 2955·2021-11-11 16:55
閱讀 515·2021-09-27 13:36
閱讀 1073·2021-09-22 15:35
閱讀 2908·2019-08-30 12:46
閱讀 3125·2019-08-26 17:02
閱讀 1826·2019-08-26 11:56
閱讀 1295·2019-08-26 11:47
閱讀 423·2019-08-23 17:01