最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Springboot-Starter造輪子之自動(dòng)鎖組件lock-starter實(shí)現(xiàn)

 更新時(shí)間:2023年05月06日 14:42:46   作者:Anoxia1  
這篇文章主要為大家介紹了Springboot-Starter造輪子之自動(dòng)鎖組件lock-starter實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

可能有人會(huì)有疑問,為什么外面已經(jīng)有更好的組件,為什么還要重復(fù)的造輪子,只能說,別人的永遠(yuǎn)是別人的,自己不去造一下,就只能知其然,而不知其所以然。(其實(shí)就為了卷)

在日常業(yè)務(wù)開發(fā)的過程中,我們經(jīng)常會(huì)遇到存在高并發(fā)的場(chǎng)景,這個(gè)時(shí)候都會(huì)選擇使用redis來實(shí)現(xiàn)一個(gè)鎖,來防止并發(fā)。

但是很多時(shí)候,我們可能業(yè)務(wù)完成后,就需要把鎖釋放掉,給下一個(gè)線程用,但是如果我們忘記了釋放鎖,可能就會(huì)存在死鎖的問題。(對(duì)于使用鎖不太熟練的話,這種情況時(shí)常發(fā)生,雖然很多時(shí)候,我們的鎖是有過期時(shí)間的,但是如果忘記了釋放,那么在這個(gè)過期時(shí)間內(nèi),還是會(huì)存在大的損失)。

還有一點(diǎn)就是,在我們使用redis實(shí)現(xiàn)一個(gè)鎖的時(shí)候,我們需要導(dǎo)入redisClient,設(shè)置key,設(shè)置過期時(shí)間,設(shè)置是否鎖等等一些重復(fù)的操作。前面的哪些步驟,很多都是重復(fù)的,所以我們可以想一個(gè)方法,來把重復(fù)的東西都抽象出來,做成統(tǒng)一的處理,同時(shí)哪些變化的值,提供一個(gè)設(shè)置的入口。

抽出來的東西,我們還可以封裝成一個(gè)spring-boot-stater,這樣我們只需要寫一份,就可以在不同的項(xiàng)目中使用了。 說干就干,下面我們使用redisson,完成一個(gè)自動(dòng)鎖的starter

實(shí)現(xiàn)

首先,我們分析一下哪些東西是我們需要進(jìn)行合并,哪些又是需要提供給使用方的。得到下面的一些問題

  • 加鎖、釋放鎖過程 我們需要合并起來
  • 鎖key,加鎖時(shí)間......這些需要給使用方注入
  • 鎖的key該怎么去生成(很多時(shí)候,我們需要根據(jù)業(yè)務(wù)字段去構(gòu)造一個(gè)key,比如 user:{userId}),那么這個(gè)userId該怎么獲???

我們從上面需要解決的問題,去思考需要怎么去實(shí)現(xiàn)。我們需要封裝一些公共的邏輯,又需要提供一些配置的入庫(kù),這樣的話,我們可以嘗試一種方法,使用 注解+AOP,通過注解的方式完成加鎖、解鎖。(很多時(shí)候,如果需要抽出一些公共的方法,會(huì)用到注解+AOP去實(shí)現(xiàn))

定義注解

AutoLock 注解

一個(gè)鎖需要有的信息有,key,加鎖的時(shí)間,時(shí)間單位,是否嘗試加鎖,加鎖等待時(shí)間 等等。(如果還有其他的業(yè)務(wù)需要,可以添加一個(gè)擴(kuò)展內(nèi)容,自己去解析處理) 那么這個(gè)注解的屬性就可以知道有哪些了

/**
 * 鎖的基本信息
 */
@Target({ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoLock {
    /**
     * 鎖前綴
     */
    String prefix() default "anoxia:lock";
    /**
     * 加鎖時(shí)間
     */
    long lockTime() default 30;
    /**
     * 是否嘗試加鎖
     */
    boolean tryLock() default true;
    /**
     * 等待時(shí)間,-1 不等待
     */
    long waitTime() default -1;
    /**
     * 鎖時(shí)間類型
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

LockField 注解

這個(gè)注解添加到參數(shù)屬性上面,用來解決上面提到獲取不同的業(yè)務(wù)參數(shù)內(nèi)容構(gòu)造key的問題。所以我們需要提供一個(gè)獲取哪些字段來構(gòu)造這個(gè)key配置,這里需要考慮兩個(gè)問題:

  • 1、參數(shù)是基本類型
  • 2、參數(shù)是引用類型 - 這種類型需要從對(duì)象中拿到對(duì)象的屬性值
/**
 * 構(gòu)建鎖的業(yè)務(wù)數(shù)據(jù)
 * @author huangle
 * @date 2023/5/5 15:01
 */
@Target({ElementType.PARAMETER})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface LockField {
    String[] fieldNames() default {};
}

定義切面

重點(diǎn)就在這個(gè)切面里面,我們需要在這里完成key的合成,鎖的獲取與釋放。整個(gè)過程可以分為以下幾步

  • 獲取鎖的基本信息,構(gòu)建key
  • 加鎖,執(zhí)行業(yè)務(wù)
  • 業(yè)務(wù)完成,釋放鎖
/**
 * 自動(dòng)鎖切面
 * 處理加鎖解鎖邏輯
 *
 * @author huangle
 * @date 2023/5/5 14:50
 */
@Aspect
@Component
public class AutoLockAspect {
    private final static Logger LOGGER = LoggerFactory.getLogger(AutoLockAspect.class);
    @Resource
    private RedissonClient redissonClient;
    private static final String REDIS_LOCK_PREFIX = "anoxiaLock";
    private static final String SEPARATOR = ":";
    /**
     * 定義切點(diǎn)
     */
    @Pointcut("@annotation(cn.anoxia.lock.annotation.AutoLock)")
    public void lockPoincut() {
    }
    /**
     * 定義攔截處理方式
     *
     * @return
     */
    @Around("lockPoincut()")
    public Object doLock(ProceedingJoinPoint joinPoint) throws Throwable {
        // 獲取需要加鎖的方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 獲取鎖注解
        AutoLock autoLock = method.getAnnotation(AutoLock.class);
        // 獲取鎖前綴
        String prefix = autoLock.prefix();
        // 獲取方法參數(shù)
        Parameter[] parameters = method.getParameters();
        StringBuilder lockKeyStr = new StringBuilder(prefix);
        Object[] args = joinPoint.getArgs();
        // 遍歷參數(shù)
        int index = -1;
        LockField lockField;
        // 構(gòu)建key
        for (Parameter parameter : parameters) {
            Object arg = args[++index];
            lockField = parameter.getAnnotation(LockField.class);
            if (lockField == null) {
                continue;
            }
            String[] fieldNames = lockField.fieldNames();
            if (fieldNames == null || fieldNames.length == 0) {
                lockKeyStr.append(SEPARATOR).append(arg);
            } else {
                List<Object> filedValues = ReflectionUtil.getFiledValues(parameter.getType(), arg, fieldNames);
                for (Object value : filedValues) {
                    lockKeyStr.append(SEPARATOR).append(value);
                }
            }
        }
        String lockKey = REDIS_LOCK_PREFIX + SEPARATOR + lockKeyStr;
        RLock lock = redissonClient.getLock(lockKey);
        // 加鎖標(biāo)志位
        boolean lockFlag = false;
        try {
            long lockTime = autoLock.lockTime();
            long waitTime = autoLock.waitTime();
            TimeUnit timeUnit = autoLock.timeUnit();
            boolean tryLock = autoLock.tryLock();
            try {
                if (tryLock) {
                    lockFlag = lock.tryLock(waitTime, lockTime, timeUnit);
                } else {
                    lock.lock(lockTime, timeUnit);
                    lockFlag = true;
                }
            }catch (Exception e){
                LOGGER.error("加鎖失??!,錯(cuò)誤信息", e);
                throw new RuntimeException("加鎖失??!");
            }
            if (!lockFlag) {
                throw new RuntimeException("加鎖失??!");
            }
            // 執(zhí)行業(yè)務(wù)
            return joinPoint.proceed();
        } finally {
            // 釋放鎖
            if (lockFlag) {
                lock.unlock();
                LOGGER.info("釋放鎖完成,key:{}",lockKey);
            }
        }
    }
}

獲取業(yè)務(wù)屬性

這個(gè)是一個(gè)獲取對(duì)象中字段的工具類,在一些常用的工具類里面也有實(shí)現(xiàn),可以直接使用也可以自己實(shí)現(xiàn)一個(gè)

/**
 * @author huangle
 * @date 2023/5/5 15:17
 */
public class ReflectionUtil {
    public static List&lt;Object&gt; getFiledValues(Class&lt;?&gt; type, Object target, String[] fieldNames) throws IllegalAccessException {
        List&lt;Field&gt; fields = getFields(type, fieldNames);
        List&lt;Object&gt; valueList = new ArrayList();
        Iterator fieldIterator = fields.iterator();
        while(fieldIterator.hasNext()) {
            Field field = (Field)fieldIterator.next();
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            Object value = field.get(target);
            valueList.add(value);
        }
        return valueList;
    }
    public static List&lt;Field&gt; getFields(Class&lt;?&gt; claszz, String[] fieldNames) {
        if (fieldNames != null &amp;&amp; fieldNames.length != 0) {
            List&lt;String&gt; needFieldList = Arrays.asList(fieldNames);
            List&lt;Field&gt; matchFieldList = new ArrayList();
            List&lt;Field&gt; fields = getAllField(claszz);
            Iterator fieldIterator = fields.iterator();
            while(fieldIterator.hasNext()) {
                Field field = (Field)fieldIterator.next();
                if (needFieldList.contains(field.getName())) {
                    matchFieldList.add(field);
                }
            }
            return matchFieldList;
        } else {
            return Collections.EMPTY_LIST;
        }
    }
    public static List&lt;Field&gt; getAllField(Class&lt;?&gt; claszz) {
        if (claszz == null) {
            return Collections.EMPTY_LIST;
        } else {
            List&lt;Field&gt; list = new ArrayList();
            do {
                Field[] array = claszz.getDeclaredFields();
                list.addAll(Arrays.asList(array));
                claszz = claszz.getSuperclass();
            } while(claszz != null &amp;&amp; claszz != Object.class);
            return list;
        }
    }
}

配置自動(dòng)注入

在我們使用 starter 的時(shí)候,都是通過這種方式,來告訴spring在加載的時(shí)候,完成這個(gè)bean的初始化。這個(gè)過程基本是定死的。 就是編寫配置類,如果通過springBoot的EnableAutoConfiguration來完成注入。注入后,我們就可以直接去使用這個(gè)封裝好的鎖了。

/**
 * @author huangle
 * @date 2023/5/5 14:50
 */
@Configuration
public class LockAutoConfig {
    @Bean
    public AutoLockAspect autoLockAspect(){
        return new AutoLockAspect();
    }
}
// spring.factories 中內(nèi)容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.anoxia.lock.config.LockAutoConfig

測(cè)試

我們先打包這個(gè)sarter,然后導(dǎo)入到一個(gè)項(xiàng)目里面(打包導(dǎo)入的過程就不說了,自己去看一下就可以) 直接上測(cè)試類,下面執(zhí)行后可以看到鎖已經(jīng)完成了釋放。如果業(yè)務(wù)拋出異常導(dǎo)致中斷也不用擔(dān)心鎖不會(huì)釋放的問題,因?yàn)槲覀兪窃?finally 中釋放鎖的

/**
 * @author huangle
 * @date 2023/5/5 14:28
 */
@RestController
@RequestMapping("/v1/user")
public class UserController {
    @AutoLock(lockTime = 3, timeUnit = TimeUnit.MINUTES)
    @GetMapping("/getUser")
    public String getUser(@RequestParam @LockField String name) {
        return "hello:"+name;
    }
    @PostMapping("/userInfo")
    @AutoLock(lockTime = 1, timeUnit = TimeUnit.MINUTES)
    public String userInfo(@RequestBody @LockField(fieldNames = {"id", "name"}) UserDto userDto){
        return userDto.getId()+":"+userDto.getName();
    }
}

總結(jié)

很多時(shí)候,一些公共的業(yè)務(wù)邏輯都可以被抽象出來成為一個(gè)獨(dú)立的組件而存在,我們可以在日常開發(fā)過程中,不斷的去思考和尋找看哪些可以被抽象出來,哪些可以更加簡(jiǎn)化一些。然后嘗試去抽象出一個(gè)組件出來,這樣的話不但可以鍛煉自己的能力,還可以得到一些很好用的工具,當(dāng)然自己抽出的組件可以存在問題,但是慢慢的鍛煉下來,總會(huì)變的越來越好。 怎么說呢,嘗試去做,能不能做好再說,做不好就一次又一次的去做。

以上就是Springboot-Starter造輪子之自動(dòng)鎖組件(lock-starter)的詳細(xì)內(nèi)容,更多關(guān)于Springboot-Starter自動(dòng)鎖組件(lock-starter)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring mvc結(jié)果跳轉(zhuǎn)方法詳解

    Spring mvc結(jié)果跳轉(zhuǎn)方法詳解

    這篇文章主要介紹了Spring mvc結(jié)果跳轉(zhuǎn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • mybatisPlus使用LocalDateTime轉(zhuǎn)化異常的實(shí)現(xiàn)

    mybatisPlus使用LocalDateTime轉(zhuǎn)化異常的實(shí)現(xiàn)

    本文主要介紹了mybatisPlus使用LocalDateTime轉(zhuǎn)化異常的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • springboot默認(rèn)掃描的路徑方式

    springboot默認(rèn)掃描的路徑方式

    這篇文章主要介紹了springboot默認(rèn)掃描的路徑方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • spring級(jí)聯(lián)屬性賦值的兩種方式解析

    spring級(jí)聯(lián)屬性賦值的兩種方式解析

    這篇文章主要介紹了spring級(jí)聯(lián)屬性賦值的兩種方式解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 如何利用泛型封裝通用的service層

    如何利用泛型封裝通用的service層

    這篇文章主要介紹了如何利用泛型封裝通用的service層,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • IDEA連接Mysql數(shù)據(jù)庫(kù)的詳細(xì)圖文教程

    IDEA連接Mysql數(shù)據(jù)庫(kù)的詳細(xì)圖文教程

    項(xiàng)目開發(fā)時(shí)使用Intellij IDEA連接本地?cái)?shù)據(jù)庫(kù),將數(shù)據(jù)庫(kù)可視化,還可對(duì)數(shù)據(jù)庫(kù)表直接進(jìn)行增刪改查操作,方便快捷又清晰,下面這篇文章主要給大家介紹了關(guān)于IDEA連接Mysql數(shù)據(jù)庫(kù)的詳細(xì)圖文教程,需要的朋友可以參考下
    2023-03-03
  • SpringBoot電腦商城項(xiàng)目刪除收貨地址的實(shí)現(xiàn)方法

    SpringBoot電腦商城項(xiàng)目刪除收貨地址的實(shí)現(xiàn)方法

    這篇文章主要介紹了SpringBoot項(xiàng)目--電腦商城刪除收貨地址功能實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • SpringBoot整合mybatis結(jié)合pageHelper插件實(shí)現(xiàn)分頁(yè)

    SpringBoot整合mybatis結(jié)合pageHelper插件實(shí)現(xiàn)分頁(yè)

    在本篇文章里小編給大家整理的是關(guān)于SpringBoot整合mybatis使用pageHelper插件進(jìn)行分頁(yè)操作相關(guān)知識(shí)點(diǎn),需要的朋友們學(xué)習(xí)下。
    2020-02-02
  • 如何只返回實(shí)體類中的部分字段問題

    如何只返回實(shí)體類中的部分字段問題

    這篇文章主要介紹了如何只返回實(shí)體類中的部分字段問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java?Bean轉(zhuǎn)Map的那些踩坑實(shí)戰(zhàn)

    Java?Bean轉(zhuǎn)Map的那些踩坑實(shí)戰(zhàn)

    項(xiàng)目中有時(shí)會(huì)遇到Map轉(zhuǎn)Bean,Bean轉(zhuǎn)Map的情況,下面這篇文章主要給大家介紹了關(guān)于Java?Bean轉(zhuǎn)Map那些踩坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07

最新評(píng)論

南岸区| 葵青区| 伊金霍洛旗| 仁怀市| 策勒县| 仁怀市| 恩平市| 剑河县| 江华| 保亭| 德格县| 商都县| 扎囊县| 鸡西市| 冷水江市| 交城县| 丰城市| 高青县| 仁寿县| 渭南市| 兰考县| 西乡县| 若尔盖县| 麦盖提县| 青川县| 天峻县| 台江县| 洪湖市| 锦屏县| 郑州市| 信宜市| 景德镇市| 依兰县| 鸡泽县| 长乐市| 安康市| 临清市| 湖州市| 镇原县| 紫金县| 楚雄市|