背景
在事务函数触发的时候添加锁,函数执行完释放锁之后,此时事务还没提交,但是由于锁释放了,此时调用get函数获取的数据还是旧的。
触发代码
如下refreshBusinessFoodCount函数中,并发请求两次,第二次get获取的数据还是旧数据(尽管添加了并发锁注解@ConcurrenLock)
@Transactional(rollbackFor = Exception.class)
@ConcurrenLock
public void refreshBusinessFoodCount() {
get();
save();
}
@Aspect
@Component
@Slf4j
@Order(0)
public class ConcurrentLockAspect {
@Autowired
private RedissonUtil redissonUtil;
@Around("@annotation(com.meal.annotation.appbusiness.ConcurrenLock)")
public Object methodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
ConcurrenLock annotation = signature.getMethod().getAnnotation(ConcurrenLock.class);
String lockKey = annotation.lockKey();
if(StringUtils.isEmpty(lockKey)) {
lockKey = Constant.REDIS_LISTENER_CONCURRENT_LOCK+signature.getName()+":";
}
Integer lockTime = annotation.lockTime();
try {
this.redissonUtil.lock(lockKey, lockTime);//锁一分钟
Object[] args = joinPoint.getArgs();
// 执行目标方法
return joinPoint.proceed(args);
}catch (Exception e){
e.printStackTrace();
throw e;
} finally {
if(this.redissonUtil.isLocked(lockKey))
this.redissonUtil.unlock(lockKey);
}
}
}
原因
- ConcurrenLock切面触发必须要在Transactional切面触发之前
- ShiroConfig的配置中引入了DefaultAdvisorAutoProxyCreator会导致Transactional多次代理,然后函数执行时会重复执行事务。
解决方法
- 注释掉shiroconfig的DefaultAdvisorAutoProxyCreator 配置
// @Bean
// @DependsOn(value = "lifecycleBeanPostProcessor")
// public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
// DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
// return defaultAdvisorAutoProxyCreator;
// }
- ConcurrentLockAspect增加@Order(0)注解,把优先级调高,让代理在Transactional的代理之前执行。