spring-事务和锁顺序的问题
经常会在事务中方法中使用锁
举个例子
@Service
public class TrxService {
@Autowired
ApplicationEventPublisher eventPublisher;
private ReentrantLock lock = new ReentrantLock(false);
@Transactional
public void order() {
lock.lock();
System.out.println("get lock");
try {
System.out.println("do order...");
eventPublisher.publishEvent(new OrderEvent(this, "temp"));
System.out.println("execute finally");
} finally {
System.out.println("release lock");
lock.unlock();
}
}
/**
* 事务监听器,监听事务提交时的事件
* @param orderEvent
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, classes = OrderEvent.class)
public void onUserRegisterEvent(OrderEvent orderEvent) {
System.out.println("orderEvent: " + orderEvent.getParam());
}
@SimpleLock
@Transactional
public void orderAop() {
System.out.println("do order...");
eventPublisher.publishEvent(new OrderEvent(this, "temp"));
System.out.println("execute finally");
}
}
@Getter
@Setter
public class OrderEvent extends ApplicationEvent {
private String param;
public OrderEvent(Object source, String param) {
super(source);
this.param = param;
}
}
测试:
@RestController
public class TrxController {
@Autowired
private TrxService trxService;
@GetMapping("/trx")
public String trx(){
trxService.order();
return "success";
}
@GetMapping("/trx/aop")
public String trxAop(){
trxService.orderAop();
return "success";
}
}
请求 /trx
, 返回:
get lock
do order...
execute finally
release lock
orderEvent: temp
可以发现, 在一个事务方法 中,事务的提交是在释放锁之后的,这里在高并发环境就很容易出现问题,比如使用分布式锁的情况
解决方案
- 将锁的获取和释放单独放在另外 一个方法中,然后调用目标事务方法,这样就能保证事务提交在锁释放之前
- 采用 aop 方式,且aop优先级要高于spring 事务的增强,即
@order(N)
N 要比 spring 事务增强器小
Aop 方式
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SimpleLock {
}
@Component
@Aspect
@Order(1) //优先级越低,先执行,最后退出,保证优先级在事务切面之前
public class AopAspect {
@Around(value = "@annotation(simpleLock)")
public Object around(ProceedingJoinPoint pjp, SimpleLock simpleLock) throws Throwable {
System.out.println("get lock");
try{
return pjp.proceed();
}finally {
System.out.println("release lock");
}
}
}
请求 /trx/aop
, 结果:
get lock
do order...
execute finally
orderEvent: temp
release lock
现在就正常了
再探索
debug 类: AbstractAdvisorAutoProxyCreator
可以发现 ,如果自定义Aspect ,没有指定 order 的话,order=Integer.MAX
,并且执行顺序在事务增强器之后
当我们指定切面 order=1
时:
望谨记!!