摘要:實現(xiàn)邏輯在請求調(diào)用層時,映射到的方法上加上注解如自定義注解防止多次提交。針對第二個問題解決方法當(dāng)然是利用代理實現(xiàn),此處利用的是的動態(tài)代理。同時利用開放的拓展處理的接口在實例化后,實例化代理。
前言
此文檔只粗略的講解實現(xiàn)思路,具體的實現(xiàn)邏輯還需要針對業(yè)務(wù)區(qū)別處理。
需求因為此業(yè)務(wù)中有讀和寫的操作,寫的執(zhí)行條件依賴于讀,并發(fā)條件下可能出現(xiàn)讀到相同的條件均可以去執(zhí)行寫操作,此時寫就會出現(xiàn)臟數(shù)據(jù),。所以項目需要實現(xiàn),在處理業(yè)務(wù)時,加鎖防止并發(fā)問題,此處利用Redis實現(xiàn),但是如果多個業(yè)務(wù)都需要這么操作的話,其實操作Redis的代碼是相同的,這樣就顯得麻煩,所以樓主采用注解的形式實現(xiàn),具體方法見下述。
實現(xiàn)邏輯在請求調(diào)用Controller層時,RequestMapping 映射到的方法上加上注解,如自定義注解 @Debounce(防止多次提交)。
此時需要考慮幾個問題1、利用Redis實現(xiàn)并發(fā)鎖的操作對Redis來說實際上就是一種Key的操作,那么自定義注解@Debounce如何實現(xiàn)key的自定義且根據(jù)參數(shù)可變化?
2、如何實現(xiàn)調(diào)用請求真實的處理方法時的攔截?
3、什么情況下才會去做這個事情?
利用處理請求的方法中的參數(shù),實現(xiàn)動態(tài)定義,此時又有個問題,就是說如果時基本數(shù)據(jù)類型+String,這樣的可以直接將值獲取拼接,但是如果參數(shù)中有對象的話,同時又想用對象中的屬性作為key值的一部分,那么直接拼接就行不通。像這種情況,統(tǒng)一的方式行不通,那么自然而然就會想到此處必須用到了拓展類,在上層只是定義這種功能,具體的實現(xiàn)由子類負(fù)責(zé)具體實現(xiàn)。(詳見后述)。
在@Debounce注解中有定義一個處理參數(shù)數(shù)組,值為處理請求的方法中的參數(shù)位置Num,從0開始依次遞增,同時也有個處理類class,作用是具體實現(xiàn)key值的拼接。
當(dāng)然是利用代理實現(xiàn),此處利用的是Spring的Cglib動態(tài)代理。同時利用Spring開放的拓展Bean處理的接口BeanPostProcessor,在bean實例化后,實例化Cglib代理。
針對第三個問題解決方法在Controller層即在有注解@Controller 或者 @RestController 的類中才會去判斷是否需要做此操作。
具體實現(xiàn)方法 Debounce 注解@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Debounce { /** * 使用的鎖鍵值 */ String lockKey() default ""; /** * 使用方法的參數(shù)(toString)做為鎖的KEY使用 * 規(guī)則:從0開始計,0表示方法中的第一個參數(shù),以此類推 * 和 lockKey 都為空時,使用方法名進(jìn)行鎖定 */ int[] methodParameters() default {}; /** * 對注釋中的參數(shù)進(jìn)行修改,默認(rèn)為字符串拼接 * * @return */ Class extends MethodParametersHandler> handler() default MethodParametersHandler.class; /** * 延時關(guān)閉,當(dāng)調(diào)用的方法結(jié)束后并不關(guān)閉鎖,只有真正超時后才關(guān)閉 * 即在鎖定時間內(nèi),只允許被調(diào)用一次 * * @return */ boolean delayClose() default false; /** * 默認(rèn)的超時時間,這個時間內(nèi)除非原來的方法調(diào)用結(jié)束,否則無法點擊 * 如果原來的方法已經(jīng)結(jié)束,時間比這個短,那么這時間無效 */ @AliasFor("lockExpireEsc") int value() default 5; /** * 鎖的超時時間 * * @return */ @AliasFor("value") int lockExpireEsc() default 5; }參數(shù)處理接口 MethodParametersHandler
此處做參數(shù)參數(shù)值的拼接同時返回拼接后的數(shù)據(jù)
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實例化后的實現(xiàn)方式-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 的分布式鎖實現(xiàn),代碼省略 , 不滿足鎖的條件可以直接返回或是拋異常 } 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 ,參數(shù)組裝的具體實現(xiàn)
@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"); } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/76537.html
摘要:數(shù)據(jù)結(jié)構(gòu)和算法樹快速排序,堆排序,插入排序其實八大排序算法都應(yīng)該了解一致性算法,一致性算法的應(yīng)用的內(nèi)存結(jié)構(gòu)。如何存儲一個的。八大排序算法一定要手敲一遍快排,堆排尤其重要。面試是一個雙向選擇的過程,不要抱著畏懼的心態(tài)去面試,不利于自己的發(fā)揮。 前言 16年畢業(yè)到現(xiàn)在也近兩年了,最近面試了阿里集團(tuán)(菜鳥網(wǎng)絡(luò),螞蟻金服),網(wǎng)易,滴滴,點我達(dá),最終收到點我達(dá),網(wǎng)易o(hù)ffer,螞蟻金服二面掛掉,...
摘要:中四種修飾符的限制范圍。數(shù)據(jù)結(jié)構(gòu)和算法樹快速排序,堆排序,插入排序其實八大排序算法都應(yīng)該了解一致性算法,一致性算法的應(yīng)用的內(nèi)存結(jié)構(gòu)。的部署方式,主從,集群。八大排序算法一定要手敲一遍快排,堆排尤其重要。 前言 15年畢業(yè)到現(xiàn)在也近三年了,最近面試了阿里集團(tuán)(菜鳥網(wǎng)絡(luò),螞蟻金服),網(wǎng)易,滴滴,點我達(dá),最終收到點我達(dá),網(wǎng)易o(hù)ffer,螞蟻金服二面掛掉,菜鳥網(wǎng)絡(luò)一個月了還在流程中...最終有...
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區(qū)分點在于工作方向的側(cè)重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預(yù)習(xí) 1.1 課程內(nèi)容分為三個模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計算機基礎(chǔ) JVM原理 多線程 設(shè)計模式 數(shù)據(jù)結(jié)構(gòu)與算法 應(yīng)用模塊: 常用工具集 ...
摘要:的簡稱,運行環(huán)境,為的運行提供了所需環(huán)境。分割字符串,返回一個分割后的字符串?dāng)?shù)組。線程安全是線程安全的,而是非線程安全的。迭代器取代了集合框架中的,迭代器允許調(diào)用者在迭代過程中移除元素。 本文分為十九個模塊,分別是:?Java 基礎(chǔ)、容器、多線程、反射、對象拷貝、Java Web 、異常、網(wǎng)絡(luò)、設(shè)計模式、Spring/Spring MVC、Spring Boot/Spring Clou...
閱讀 2979·2021-11-11 16:55
閱讀 546·2021-09-27 13:36
閱讀 1123·2021-09-22 15:35
閱讀 2951·2019-08-30 12:46
閱讀 3157·2019-08-26 17:02
閱讀 1858·2019-08-26 11:56
閱讀 1319·2019-08-26 11:47
閱讀 443·2019-08-23 17:01