1. 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.27.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 实现
2.1 锁注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RLock {
String key();
}
2.2 RedissonConfig
import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ConfigurationProperties(prefix = "spring.redis")
@Configuration
@Data
public class RedissonConfig {
// 读取配置文件里面的Redis信息
private String host;
private String port;
private String password;
/**
* 配置redisson
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
String address = "redis://" + host + ":" + port;
//使用json序列化方式
config.setCodec(new JsonJacksonCodec());
config.useSingleServer().setAddress(address).setPassword(password);
return Redisson.create(config);
}
}
2.3 AOP切面, 拦截需要使用锁的方法
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
@Component
@Aspect
public class RLockAspect {
@Resource
private RedissonClient redissonClient;
@Around("@annotation(rLock)")
public Object before(ProceedingJoinPoint point, RLock rLock){
String key = rLock.key();
Object proceed = null;
org.redisson.api.RLock lock = null;
try {
// 获取锁
lock = redissonClient.getLock(key);
// 尝试加锁
if (lock.tryLock()) {
// 加锁成功, 执行目标方法
proceed = point.proceed();
} else {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
// 获取方法
Method method = methodSignature.getMethod();
System.out.println("该方法没有执行--> " + method.getName());
}
} catch (Throwable e) {
throw new RuntimeException(e);
} finally { // 释放锁
// 解锁前检查当前线程是否持有该锁
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return proceed;
}
}
3. 测试
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class TimerTest {
@RLock(key = "cn:ac:zing:lock:test")
@Scheduled(cron = "0/10 * * * * ?")
public void lock1() {
String date = LocalDateTime.now().toString();
System.out.println(date + " lock1 执行");
}
@RLock(key = "cn:ac:zing:lock:test")
@Scheduled(cron = "0/10 * * * * ?")
public void lock2() {
String date = LocalDateTime.now().toString();
System.out.println(date + " lock2 执行");
}
}
控制台输出: