国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Controller層利用Redis實現分布式鎖(注解實現)

kevin / 2836人閱讀

摘要:實現邏輯在請求調用層時,映射到的方法上加上注解如自定義注解防止多次提交。針對第二個問題解決方法當然是利用代理實現,此處利用的是的動態代理。同時利用開放的拓展處理的接口在實例化后,實例化代理。

前言

此文檔只粗略的講解實現思路,具體的實現邏輯還需要針對業務區別處理。

需求

因為此業務中有讀和寫的操作,寫的執行條件依賴于讀,并發條件下可能出現讀到相同的條件均可以去執行寫操作,此時寫就會出現臟數據,。所以項目需要實現,在處理業務時,加鎖防止并發問題,此處利用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 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 Map, 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 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鎖
            }
        }
    }
}
Bean實例化后的實現方式-BeanPostProcessor
@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

相關文章

  • 一個兩年Java的面試總結

    摘要:數據結構和算法樹快速排序,堆排序,插入排序其實八大排序算法都應該了解一致性算法,一致性算法的應用的內存結構。如何存儲一個的。八大排序算法一定要手敲一遍快排,堆排尤其重要。面試是一個雙向選擇的過程,不要抱著畏懼的心態去面試,不利于自己的發揮。 前言 16年畢業到現在也近兩年了,最近面試了阿里集團(菜鳥網絡,螞蟻金服),網易,滴滴,點我達,最終收到點我達,網易offer,螞蟻金服二面掛掉,...

    anRui 評論0 收藏0
  • 【薦】令人心情愉悅的一次面試總結

    摘要:中四種修飾符的限制范圍。數據結構和算法樹快速排序,堆排序,插入排序其實八大排序算法都應該了解一致性算法,一致性算法的應用的內存結構。的部署方式,主從,集群。八大排序算法一定要手敲一遍快排,堆排尤其重要。 前言 15年畢業到現在也近三年了,最近面試了阿里集團(菜鳥網絡,螞蟻金服),網易,滴滴,點我達,最終收到點我達,網易offer,螞蟻金服二面掛掉,菜鳥網絡一個月了還在流程中...最終有...

    20171112 評論0 收藏0
  • Java面試 32個核心必考點完全解析

    摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數據結構與算法 應用模塊: 常用工具集 ...

    JiaXinYi 評論0 收藏0
  • Java 最常見 200+ 面試題全解析:面試必備(附答案)

    摘要:的簡稱,運行環境,為的運行提供了所需環境。分割字符串,返回一個分割后的字符串數組。線程安全是線程安全的,而是非線程安全的。迭代器取代了集合框架中的,迭代器允許調用者在迭代過程中移除元素。 本文分為十九個模塊,分別是:?Java 基礎、容器、多線程、反射、對象拷貝、Java Web 、異常、網絡、設計模式、Spring/Spring MVC、Spring Boot/Spring Clou...

    hufeng 評論0 收藏0

發表評論

0條評論

kevin

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<