스프링 초기화 시점에는 트랜잭션 AOP가 적용되지 않을 수 있다.
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.annotation.PostConstruct;
@SpringBootTest
public class InitTxTest {
@Autowired
Hello hello;
@Test
void go() {
// Hello > initV1메서드는 스프링 초기화 시점에 호출한다.
}
@TestConfiguration
static class InitTxTestConfig {
@Bean
public Hello hello() {
return new Hello();
}
}
@Slf4j
static class Hello {
@PostConstruct
@Transactional
public void initV1() {
boolean actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("hello init @PostConstruct Tx active={}", actualTransactionActive);
}
@EventListener(ApplicationReadyEvent.class)
@Transactional
public void initV2() {
boolean actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("hello init ApplicationReadyEvent Tx active={}", actualTransactionActive);
}
}
}
go() 테스트코드 실행
초기화 코드(예: @PostConstruct )와 @Transactional 을 함께 사용하면 트랜잭션이 적용되지 않는다.
@PostConstruct
@Transactional
public void initV1() {
boolean actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("hello init @PostConstruct Tx active={}", actualTransactionActive);
}
왜냐하면 초기화 코드가 먼저 호출되고, 그 다음에 트랜잭션 AOP가 적용되기 때문이다. 따라서 초기화 시점에는 해당 메서드에서 트랜잭션을 획득할 수 없다.
실행 로그
Hello init @PostConstruct tx active=false
해결방안
가장 확실한 대안은 ApplicationReadyEvent 이벤트를 사용하는 것이다.
@EventListener(ApplicationReadyEvent.class)
@Transactional
public void initV2() {
boolean actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("hello init ApplicationReadyEvent Tx active={}", actualTransactionActive);
}
이 이벤트는 트랜잭션 AOP를 포함한 스프링이 컨테이너가 완전히 생성되고 난 다음에 이벤트가 붙은 메서드를 호출해준다. 따라서 init2() 는 트랜잭션이 적용된 것을 확인할 수 있다.
실행 로그
TransactionInterceptor : Getting transaction for [Hello.init2]
..ngtx.apply.InitTxTest$Hello : Hello init ApplicationReadyEvent tx active=true
TransactionInterceptor : Completing transaction for [Hello.init2]
References 및 사진 출처
김영한의 스프링 DB 2편 - 데이터 접근 활용 기술
'Spring > Transaction' 카테고리의 다른 글
| 트랜잭션 전파 (0) | 2023.09.05 |
|---|---|
| Spring Transaction 트랜잭션 옵션 (0) | 2023.08.16 |
| Spring Transaction AOP 주의사항 - 프록시 내부호출 문제해결 (0) | 2023.08.15 |
| Spring Transaction AOP 주의사항 - 프록시 내부호출 문제 (0) | 2023.08.15 |
| Spring Transaction 우선순위 (0) | 2023.08.15 |