之前写过一个分布式锁,应该是参考了比较落后的redis版本 这次会在之前的基础之上用aop进行封装并简化代码
重复的内容不再赘述,具体的参考一下链接
https://blog.csdn.net/qq_36559868/article/details/98174986
maven配置一下aop 和redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
上代码
1.设置一下redis的序列化方式 默认是java原生的序列化 (这一步不是重点根据具体业务场景自己设置)
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<Object,Object> redisTemplate=new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper=new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
2.分布式锁内容
@Slf4j
@Component
public class RedisLock {
@Autowired
private RedisTemplate redisTemplate;
/**
* 带超时时间的锁
*
* @param expireTime
* @param lockKey
* @param lockValue
* @return
*/
public Boolean trylockWithTime(Long expireTime, String lockKey, String lockValue,TimeUnit timeUnit) {
//过期时间点
Long invalidTime = System.currentTimeMillis() + expireTime * 1000;
//不断尝试获取锁 直到超时
boolean flag = false;
while (System.currentTimeMillis() < invalidTime) {
flag = tryLock(lockKey, lockValue, expireTime,timeUnit);
if (flag) {
log.info("获得锁成功");
break;
}
Thread.sleep(1);
}
if (flag==false){
log.info("竞争锁失败");
}
return flag;
}
/**
* 解锁
*
* @param retryTimes
* @param lockValue
* @param lockKey
* @return
*/
public Boolean unlock(Integer retryTimes, String lockValue, String lockKey) {
String valueOfKey = (String) redisTemplate.opsForValue().get(lockKey);
Boolean flag = false;
if (lockValue.equals(valueOfKey)) {
while (retryTimes > 0) {
try {
flag = redisTemplate.delete(lockKey);
log.info("释放锁成功");
break;
} catch (Exception e) {
log.error("解锁失败");
retryTimes--;
}
}
}
return flag;
}
/**
* 上锁
*
* @param lockKey
* @param lockValue
* @param expireTime
* @return
*/
private Boolean tryLock(String lockKey, String lockValue, Long expireTime,TimeUnit timeUnit) {
return redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, expireTime, timeUnit);
}
}
3.创建注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisAsync {
/**
* 锁名称
* @return
*/
String key() default "";
/**
* 超时时间
* @return
*/
long expire() default 0L;
/**
* 时间单位
* @return
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* 重试次数
* @return
*/
int retryTimes() default 3;
}
4.aop操作
@Component
@Aspect
public class AspectForLock {
@Autowired
private RedisLock redisLock;
@Pointcut("@annotation(com.hzc.commonutil.lock.RedisAsync)")
public void cut() {
}
@Around("cut()&&@annotation(redisAsync)")
public void around(ProceedingJoinPoint joinPoint, RedisAsync redisAsync) throws Throwable {
Long expire = redisAsync.expire();
String lockKey = redisAsync.key();
TimeUnit timeUnit = redisAsync.timeUnit();
int retryTimes = redisAsync.retryTimes();
//这个值可以根据业务定制 必须唯一
String lockValue= UUID.randomUUID().toString();
Boolean flag = redisLock.trylockWithTime(expire, lockKey, lockValue, timeUnit);
if (!flag) {
return;
}
try {
joinPoint.proceed();
} catch (Exception e) {
e.printStackTrace();
}finally {
redisLock.unlock(retryTimes, lockValue, lockKey);
}
}
}
5.使用
@Component
public class TestMain {
@RedisAsync(key = "test",expire = 10L,timeUnit = TimeUnit.SECONDS,retryTimes = 3)
public void xxx() throws InterruptedException {
Thread.sleep(5000L);
}
}
6.测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class CommonutilApplicationTests {
@Autowired
private TestMain testMain;
@Test
public void contextLoads() throws InterruptedException {
testMain.xxx();
}
}
7.结果