在Spring Boot应用中,结合Redisson和自定义注解可以实现分布式锁的功能,从而在多线程或微服务环境下同步访问共享资源。下面是一个使用Redisson和Spring Boot 结合的案例,用于实现分布式锁。
1. 添加依赖
首先,需要在项目的pom.xml
文件中添加Redisson Spring Boot Starter依赖。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.1</version>
</dependency>
2. 配置Redisson
在application.yml
或application.properties
中配置Redisson客户端。
spring:
redis:
host: 127.0.0.1
port: 6379
password: your-redis-password
redisson:
file: classpath:redisson.yml
redisson.yml
配置文件内容:
redisson:
threads: 0
codec: org.redisson.codec.JsonJacksonCodec
transportMode: NIO
address: "redis://127.0.0.1:6379"
password: your-redis-password
3. 创建自定义注解
定义一个@RedisLock
注解,用于标记需要加锁的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
String value() default "";
long waitTime() default 10;
long leaseTime() default 10;
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
4. 创建注解处理器
创建一个RedisLockAnnotationProcessor
类,用于处理@RedisLock
注解。
@Component
public class RedisLockAnnotationProcessor implements MethodInterceptor {
@Autowired
private RedissonClient redissonClient;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
RedisLock redisLock = method.getAnnotation(RedisLock.class);
if (redisLock != null) {
String lockKey = "lock:" + redisLock.value().isEmpty() ? method.getName() : redisLock.value();
RLock lock = redissonClient.getLock(lockKey);
try {
if (!lock.tryLock(redisLock.waitTime(), redisLock.leaseTime(), redisLock.timeUnit())) {
throw new ServiceException("锁被占用,请稍后提交!");
}
return invocation.proceed();
} finally {
lock.unlock();
}
}
return invocation.proceed();
}
}
RedisLockAnnotationProcessor
是一个运行时注解处理器,它实现了 Spring AOP 的 MethodInterceptor
接口。这个处理器将在方法执行前后拦截那些标记有 @RedisLock
注解的方法,并执行与分布式锁相关的逻辑。
下面是对这个注解处理器的详细解析:
@Component
:这个注解表明RedisLockAnnotationProcessor
类是一个 Spring 组件,Spring 容器应该创建它的实例并管理它的生命周期。public class RedisLockAnnotationProcessor implements MethodInterceptor
:这个类实现了MethodInterceptor
接口,这意味着它将拦截方法调用并可以定义方法执行前后的行为。@Autowired
:这个注解自动注入了一个RedissonClient
实例,用于与 Redisson 服务器进行交互,如获取分布式锁。public Object invoke(MethodInvocation invocation) throws Throwable
:这是MethodInterceptor
接口的核心方法,它在目标方法被调用之前和之后执行。方法的返回值是Object
类型,表示它可以返回任何类型的值。Method method = invocation.getMethod();
:获取当前被拦截的方法的信息。RedisLock redisLock = method.getAnnotation(RedisLock.class);
:检查当前方法是否使用了@RedisLock
注解。if (redisLock != null) { ... }
:如果方法上有@RedisLock
注解,执行相关的逻辑。String lockKey = "lock:" + redisLock.value().isEmpty() ? method.getName() : redisLock.value();
:构造锁的键名,如果@RedisLock
注解的value
属性为空,则使用方法名作为锁的键。RLock lock = redissonClient.getLock(lockKey);
:通过 Redisson 客户端获取一个分布式锁实例。if (!lock.tryLock(redisLock.waitTime(), redisLock.leaseTime(), redisLock.timeUnit())) { ... }
:尝试获取锁,如果无法获取锁,则抛出异常。return invocation.proceed();
:如果成功获取锁,继续执行原方法。finally { lock.unlock(); }
:无论方法执行是否成功,最终释放锁。
这个注解处理器的逻辑确保了在执行被 @RedisLock
注解的方法时,会先尝试获取一个分布式锁,以防止并发访问造成的问题。如果获取锁失败,则会抛出异常,否则执行原方法并在执行结束后释放锁。这种方式在分布式系统中非常有用,特别是在需要确保操作的原子性和一致性的场景中。
5. 配置注解驱动
在Spring Boot应用中启用注解驱动。
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class RedisLockConfig {
@Bean
public RedisLockAnnotationProcessor redisLockAnnotationProcessor() {
return new RedisLockAnnotationProcessor();
}
@Bean
public Advisor advisor(RedisLockAnnotationProcessor processor) {
Pointcut pointcut = AspectJExpressionPointcut.parseExpression("execution(* *(..))");
return new DefaultAdvisor(pointcut, processor);
}
}
这段代码是一个Spring Boot配置类,用于配置AspectJ切面以支持自定义注解的处理。下面是对代码的逐行解析:
@Configuration
:这是一个Spring注解,用于定义一个配置类。配置类中可以包含Bean的定义,这些Bean将被Spring容器管理。@EnableAspectJAutoProxy(exposeProxy = true)
:这是Spring的切面注解,用于启用AspectJ自动代理。这意味着Spring会自动为匹配的Bean创建代理,以便可以在运行时应用切面逻辑。exposeProxy
属性设置为true
表示将代理对象暴露给被代理的Bean,这允许被代理的Bean访问自己的代理(例如,用于日志记录或性能监控)。public class RedisLockConfig
:这是配置类的名字。@Bean
:这是Spring的注解,用于定义一个Bean。在Spring容器启动时,会调用带有@Bean
注解的方法,并将其返回值注册为Spring容器中的Bean。public RedisLockAnnotationProcessor redisLockAnnotationProcessor()
:这是一个Bean的定义方法。它返回一个RedisLockAnnotationProcessor
类型的实例,这个类可能是一个自定义的注解处理器,用于处理特定的注解(例如,用于处理分布式锁的注解)。return new RedisLockAnnotationProcessor();
:这行代码创建并返回了一个RedisLockAnnotationProcessor
实例。@Bean
:这是另一个Bean的定义方法。public Advisor advisor(RedisLockAnnotationProcessor processor)
:这个方法定义了一个Advisor
Bean,它由两个主要部分组成:Pointcut和Advice(在这里是通过RedisLockAnnotationProcessor
实例提供的)。Pointcut pointcut = AspectJExpressionPointcut.parseExpression("execution(* *(..))");
:这行代码创建了一个Pointcut实例,它定义了切面应该应用的位置。这里使用的是AspectJ表达式,表示对所有方法的执行进行匹配。return new DefaultAdvisor(pointcut, processor);
:这行代码创建并返回了一个DefaultAdvisor
实例,它将前面定义的Pointcut和RedisLockAnnotationProcessor实例作为参数。DefaultAdvisor
是一个Advisor的具体实现,它告诉Spring AOP在哪些JoinPoint(连接点,即方法的执行)上应用哪个Advice。
这段代码定义了一个Spring配置类RedisLockConfig
,它注册了两个Bean:一个RedisLockAnnotationProcessor
用于处理特定的注解,和一个Advisor
用于指定切面逻辑应该应用的位置。通过这种方式,Spring Boot应用可以在运行时自动处理带有特定注解的方法,例如实现分布式锁的功能。
6. 使用注解
在需要加锁的方法上使用@RedisLock
注解。
@RestController
public class MyController {
@RedisLock("user:lock", waitTime = 5, leaseTime = 10, timeUnit = TimeUnit.SECONDS)
@GetMapping("/user")
public String getUser() {
// 业务逻辑处理
return "User Info";
}
}
总结
通过上述步骤,我们成功地将Redisson与Spring Boot结合起来,实现了一个简单的分布式锁功能。开发者只需在方法上添加@RedisLock
注解,并设置相应的参数,就可以控制并发访问,避免数据不一致的问题。这种机制在处理共享资源或执行幂等性操作时非常有用。同时,使用注解的方式也让代码更加简洁和易于维护。