自定义starter,注解,实现分布式锁

某乎我也写了,如果想直接用代码请直接搜索这个文章标题的某乎
自定义RedissonClient-starter
依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.26</version> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.15.6</version> </dependency> </dependencies>
基础配置类
ConfigurationProperties装载的时候会在properties/yaml/yml文件中找到前缀位my:caplock之后的配置注解。
请注意:对于装配的时候如果默认会my:caplock之后一级属性的作为配置类的的属性,如果存在my:caplock:lock1:one这种二级属性,此时one会作为lock1(可以看作新类)的一个属性,并且会存在装载配置问题。原因去看springBoot的中文文档,可以去看看springboot的外部配置这一板块
@EnableConfigurationProperties这种和@Value的区别之一是后者可以实现spel表达式的识别,文章下面的自定义注解这种就需要用对应的paser来进行执行语句表达获取value
SPEL:核心技术_Spring 框架文档 (springref.com)
@Setter @Getter @ConfigurationProperties("my.caplock") public class RedisConfig { private String url; private boolean usage; }
配置类
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(RedisConfig.class) @Log4j2 public class MyRedisCapLockConfig { @Bean public RedissonClient redissonClient(RedisConfig redisConfig) { if (redisConfig.isUsage()==false){ log.info("不启用redis分布式锁"); return null; } Config config = new Config(); config.setTransportMode(TransportMode.NIO); SingleServerConfig singleServerConfig = config.useSingleServer(); singleServerConfig.setAddress(redisConfig.getUrl()); return Redisson.create(config); } }
在当前项目的resources\META-INF的文件夹下出创建spring.factories并附上以下内容。
org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.luyu.config.MyRedisCapLockConfig
点击idea下的将对应的maven模块的生命周期进行install保存该项目到本地仓库中

编辑
自定义注解
注意:请在启动类上加上扫描组件的注解,防止spring没有扫描到我们封装的bean,还有开启切面代理
@SpringBootApplication @ComponentScan({"com.luyu"}) @EnableAspectJAutoProxy public class RedisLockApplication { public static void main(String[] args) { SpringApplication.run(RedisLockApplication.class, args); } }

编辑切换为居中
启动项目依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.luyu</groupId> <artifactId>myRedisStarter</artifactId> <version>1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
目前我们实现的是一个实现分布式锁功能的注解Lock
import java.lang.annotation.*; /** * @Author luYu * @Date 2023/7/8 13:19 */ @Retention(RetentionPolicy.RUNTIME) //在执行时有效 @Target({ElementType.METHOD}) //允许在方法注解 @Repeatable(Lock.Locks.class) //允许重复注解,前提必须是实现了拥有该目标注解的名字为value的注释数组 @Documented //javadoc 上下文运行 @Inherited // 继承 public @interface Lock { // 锁住的key String lockKey()default ""; // 锁住的时间 int lockTime()default 5; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited @Documented public @interface Locks { Lock[] value(); } }
相关切面
@Component @Aspect public class LockAspect { @Resource private RedissonClient redissonClient; @Around("@annotation(com.luyu.redislock.anno.Lock)||@annotation(com.luyu.redislock.anno.Lock.Locks)") public void getvoid(ProceedingJoinPoint joinPoint){ MethodSignature signature = ((MethodSignature) joinPoint.getSignature()); Lock[] annos = signature.getMethod().getAnnotationsByType(Lock.class); getLock(toLock -> { ArrayList<RLock> rLocks = new ArrayList<>(); for (Lock anno : toLock) { RLock lock = redissonClient.getLock(anno.lockKey()); try { boolean b = lock.tryLock(anno.lockTime(), TimeUnit.SECONDS); if (!b){ throw new RuntimeException("上锁失败:"+lock.getName()); } rLocks.add(lock); joinPoint.proceed(); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException("上锁失败:"+lock.getName()); } catch (Throwable e) { throw new RuntimeException(e); } finally { unLock(rLocks); } } }, annos); } public static void getLock(Consumer<Lock[]> consumer, Lock... locks) { consumer.accept(locks); } public static void unLock(ArrayList<RLock> rLocks) { if (rLocks.isEmpty()) { return; } for (RLock rLock : rLocks) { if (rLock.isLocked() && rLock.isHeldByCurrentThread()){ rLock.unlock(); System.out.println("解锁:"+rLock.getName()); } } } }
接口
@RestController @RequestMapping("/1") public class LockController { @GetMapping("/ad") @Lock(lockKey = "luyu",lockTime = 4) @Lock(lockKey = "luyu1",lockTime = 3) @SneakyThrows public void getLock(){ try { TimeUnit.SECONDS.sleep(2); } finally { System.out.println("鲈鱼来了"); } } }
结果:
因为我在aop重复执行joinPoint.proceed();就输出了两次鲈鱼来了,不要在意细节哈哈。

编辑切换为居中
给大家留了一个坑,@SneakyThrows和很多catach,可以去了解一下。
觉得可以的话点个赞是我持续分享的动力(努力提高我分享的水平)