아직 Spring으로 공부하거나 개발을 하면서 해당 트랜잭션 AOP 문제를 마주한 적은 없다. 하지만 이 문제는 실무에서 많이 마주친다고 강조하셔서, 그 때를 대비에 포스팅하려고 한다.
먼저 @Transactional을 사용하면 프록시 방식의 AOP인 스프링 트랜잭션 AOP가 적용된다. 따라서 프록시 객체가 요청을 먼저 받고 트랜잭션을 처리하고 실제 객체를 호출한다. 만일 프록시를 거치지 않고 대상 객체를 직접 호출하면 AOP가 적용되지 않아 트랜잭션 또한 적용되지 않는다.
@Slf4j
class CallService {
public void f1() {
log.info("call f1");
printTxInfo();
}
@Transactional
public void f2() {
log.info("call f2");
printTxInfo();
}
public void f3() {
log.info("call f3");
printTxInfo();
f2();
}
public void printTxInfo() {
boolean txActive
= TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active = {}", txActive);
}
}
@Test
void F1Call() {
callService.f1();
}
트랜잭션 적용 X
@Test
void F2Call() {
callService.f2();
}
트랜잭션 적용 O
@Test
void F3Call() {
callService.f3();
}
f3() f2() 모두 트랜잭션 적용 X ➝ 메서드 내부 호출
@Slf4j
@RequiredArgsConstructor
static class F3Service {
private final F2Service internalService;
public void f3() {
log.info("call f3");
printTxInfo();
internalService.f2();
}
public void printTxInfo() {
boolean txActive
= TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active = {}", txActive);
}
}
@Slf4j
static class F2Service {
@Transactional
public void f2() {
log.info("call f2");
printTxInfo();
}
public void printTxInfo() {
boolean txActive
= TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active = {}", txActive);
}
}
f2()에 트랜잭션이 잘 적용되는 것을 확인할 수 있다.