legacy/Spring

[Spring] @PostConstruct와 @Transactional

heemang_e 2023. 9. 5. 01:47

 

@PostConstruct와 @Transactional이 동시에 사용되었을 때 트랜잭션이 적용되지 않는다. 그 이유는 두 애너테이션이 호출되는 시점이 다르기 때문이다.

 

스프링 생명 주기

스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존 관계 주입 -> 초기화 콜백 -> 사용 -> 소멸 콜백 -> 스프링 컨테이너 종료

@PostConstruct 호출 시점

스프링 빈이 생성 되고 의존 관계가 주입된 후에 호출된다.

@Transactional 호출 시점

초기화 콜백 이후 컨테이너가 초기화된 후에 호출된다.


코드

Hello 클래스가 빈으로 등록되어 있고, initV1() 메서드에 @PostConstruct 애너테이션이 사용되고 있다.

@SpringBootTest
@Slf4j
public class InitTxTest {

    @Autowired
    Hello hello;

    @Test
    void init() {
    }

    @TestConfiguration
    static class ConfigClass {
        @Bean
        public Hello hello() {
            System.out.println("Hello 의존 관계 주입");
            return new Hello();
        }
    }

    static class Hello {
        @PostConstruct
        @Transactional
        public void initV1() {
            log.info("Hello init @PostConstruct tx active={}", TransactionSynchronizationManager.isActualTransactionActive());
        }
    }
}

 

위에서 말했듯이 @PostConstruct는 의존 관계 주입 완료된 후 호출이 되고, @Transactional은 스프링 컨테이너가 초기화된 후 호출된다.

 

따라서 다음과 같은 결과를 얻을 수 있다.

  1. @Autowired를 통해 의존 관계가 주입시에 "Hello 의존 관계 주입"이 먼저 출력이 된다.
  2. 의존 관계 주입이 끝났기때문에 초기화 콜백이 일어난다. 이때 @PostConstruct가 호출된다. 따라서 "Hello init @PostConstruct.."가 출력된다.
  3. 스프링 컨테이너가 초기화된다. "Started InitTxTest..."가 출력된다.


@PostConstruct와 @Transactional 호출 시점이 다르다.

@PostConstruct는 의존관계 주입이 완료된 후 호출되고, @Transactional은 스프링 컨테이너가 초기화된 시점에 호출된다. 따라서 두 애너테이션을 동시에 사용하면 @PostConstruct가 먼저 호출되기 때문에 트랜잭션이 정상적으로 처리되지 않는다.

 

트랜잭션의 경우 AOP 기술을 사용한다. AOP의 경우에는 스프링 컨테이너가 완전히 초기화되어야 사용할 수 있다. 

따라서 @PostConstruct로 인해 스프링 컨테이너가 올라오지 않은 상태에서 @Transactional을 사용할 수 없다.

 

참고

https://www.inflearn.com/questions/26902/postconstruct%EC%99%80-transactional-%EB%B6%84%EB%A6%AC