成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

kevin / 2860人閱讀

摘要:實現(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 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 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 的分布式鎖實現(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鎖
            }
        }
    }
}
Bean實例化后的實現(xiàn)方式-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 ,參數(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

相關(guān)文章

  • 一個兩年Java的面試總結(jié)

    摘要:數(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,螞蟻金服二面掛掉,...

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

    摘要:中四種修飾符的限制范圍。數(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ò)一個月了還在流程中...最終有...

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

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

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

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

    hufeng 評論0 收藏0

發(fā)表評論

0條評論

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