트랜잭션 - 적용 확인

박찬우·2024년 2월 8일

스프링 DB

목록 보기
37/53

적용 확인

  • Test
    • 트랜잭션 호출 로그 확인(application.properties)
      • logging.level.org.springframework.transaction.interceptor=TRACE
@Slf4j
@SpringBootTest
public class TxBasicTest {

    @Autowired
    BasicService basicService;

    @Test
    void proxyCheck() {
    	// 트랜잭션 적용 시 basicService에 대한 프록시 객체를 생성함
        log.info("aop class={}", basicService.getClass());
        Assertions.assertThat(AopUtils.isAopProxy(basicService)).isTrue();
    }

    @Test
    void txText() {
        basicService.tx();
        basicService.nonTx();
    }

    @TestConfiguration
    static class TxApplyBasicConfig {
        @Bean
        BasicService basicService() {
            return new BasicService();
        }
    }

    @Slf4j
    static class BasicService {

    	// 트랜잭션 적용
        @Transactional
        public void tx() {
            log.info("call tx");
            boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
            log.info("tx active={}", txActive);
        }

		// 트랜잭션 미적용
        public void nonTx() {
            log.info("call nonTx");
            boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
            log.info("tx active={}", txActive);
        }
    }
}

  • AopUtils.isAopProxy()
    • 선언적 트랜잭션 방식에서 스프링 트랜잭션은 AOP를 기반으로 동작한다. @Transactional 을 메서드나 클래스에 붙이면 해당 객체는 트랜잭션 AOP 적용의 대상이 되고, 결과적으로 실제 객체 대신에 트랜잭션을 처리해주는 프록시 객체가 스프링 빈에 등록된다. 그리고 주입을 받을 때도 실제 객체 대신에 프록시 객체가 주입된다.
    • getClass() = aop class=class hello.springtx.apply.TxBasicTest$BasicService$$SpringCGLIB$$0
  • 프록시 등록 과정
    • @Transactional 애노테이션이 특정 클래스나 메서드에 하나라도 있으면 트랜잭션 AOP는 프록시를 만들어서 스프링 컨테이너에 등록한다. 그리고 실제 basicService 객체 대신에 프록시인 basicService$ $CGLIB 를 스프링 빈에 등록한다. 그리고 프록시는 내부에 실제 basicService 를 참조하게 된다. 여기서 핵 심은 실제 객체 대신에 프록시가 스프링 컨테이너에 등록되었다는 점이다.
    • 클라이언트인 txBasicTest 는 스프링 컨테이너에 @Autowired BasicService basicService 로 의 존관계 주입을 요청한다. 스프링 컨테이너에는 실제 객체 대신에 프록시가 스프링 빈으로 등록되어 있기 때문에 프록시를 주입한다.
    • 프록시는 BasicService 를 상속해서 만들어지기 때문에 다형성을 활용할 수 있다. 따라서 BasicService 대신에 프록시인 BasicService$$CGLIB 를 주입할 수 있다.
  • 프록시 동작 과정
    • 트랜잭션 적용
      • 클라이언트가 basicService.tx() 를 호출하면, 프록시의 tx() 가 호출된다. 여기서 프록시는 tx() 메서 드가 트랜잭션을 사용할 수 있는지 확인해본다. tx() 메서드에는 @Transactional 이 붙어있으므로 트랜잭 션 적용 대상이다.
      • 따라서 트랜잭션을 시작한 다음에 실제 basicService.tx() 를 호출한다.
      • 그리고 실제 basicService.tx() 의 호출이 끝나서 프록시로 제어가(리턴) 돌아오면 프록시는 트랜잭션 로직 을 커밋하거나 롤백해서 트랜잭션을 종료한다.
    • 트랜잭션 미적용
      • 클라이언트가 basicService.nonTx() 를 호출하면, 트랜잭션 프록시의 nonTx() 가 호출된다. 여기서 nonTx() 메서드가 트랜잭션을 사용할 수 있는지 확인해본다. nonTx() 에는 @Transactional 이 없으므 로 적용 대상이 아니다.
      • 따라서 트랜잭션을 시작하지 않고, basicService.nonTx() 를 호출하고 종료한다.
    • TransactionSynchronizationManager.isActualTransactionActive()
      • 현재 쓰레드에 트랜잭션이 적용되어 있는지 확인할 수 있는 기능이다. 결과가 true 면 트랜잭션이 적용되어 있는 것이다. 트랜잭션의 적용 여부를 가장 확실하게 확인할 수 있다.
profile
진짜 개발자가 되어보자

0개의 댓글