트랜잭션 - AOP(프록시 내부 호출 해결)

박찬우·2024년 2월 8일

스프링 DB

목록 보기
40/53

프록시 내부 호출 해결

  • 메서드 내부 호출 때문에 트랜잭션 프록시가 적용되지 않는 문제를 해결하기 위해 해당 메서드를 별도의 클래스로 분리하여 해결한다
  • 다른 방법들도 있지만 주로 이 방법으로 해결함(편하기 때문임)

예)

@Slf4j
@SpringBootTest
public class InternalCallV2Test {

    @Autowired
    CallService callService;

    @Test
    void printProxy() {
        log.info("call service={}", callService.getClass());
    }

    @Test
    void externalCall() {
        callService.external();
    }

    @TestConfiguration
    static class InternalCallV1TestConfig {
        @Bean
        CallService callService() {
            return new CallService(internalService());
        }

        @Bean
        InternalService internalService() {
            return new InternalService();
        }
    }

    @Slf4j
    @RequiredArgsConstructor
    static class CallService {

        private final InternalService internalService;
        public void external() {
            log.info("call external");
            printTxInfo();
            internalService.internal();
        }

        private void printTxInfo() {
            boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
            log.info("tx active={}", txActive);

            boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
            log.info("tx readOnly={}", readOnly);
        }
    }

    static class InternalService {

        @Transactional
        public void internal() {
            log.info("call internal");
            printTxInfo();
        }

        private void printTxInfo() {
            boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
            log.info("tx active={}", txActive);

            boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
            log.info("tx readOnly={}", readOnly);
        }
    }
}

  • 동작
    1. 클라이언트인 테스트 코드는 callService.external() 을 호출한다.
    2. callService 는 실제 callService 객체 인스턴스이다.
    3. callService 는 주입 받은 internalService.internal() 을 호출한다.
    4. internalService 는 트랜잭션 프록시이다. internal() 메서드에 @Transactional 이 붙어 있으 므로 트랜잭션 프록시는 트랜잭션을 적용한다.
    5. 트랜잭션 적용 후 실제 internalService 객체 인스턴스의 internal() 을 호출한다.

public 메서드

  • 스프링의 트랜잭션 AOP 기능은 public 메서드에만 트랜잭션을 적용하도록 기본 설정이 되어있다(스프링 3.0 부터는 protected , package-visible (default 접근제한자) 적용됨)
  • 이렇게 클래스 레벨에 트랜잭션을 적용하면 모든 메서드에 트랜잭션이 걸릴 수 있다. 그러면 트랜잭션을 의도하지 않는 곳 까지 트랜잭션이 과도하게 적용된다. 트랜잭션은 주로 비즈니스 로직의 시작점에 걸기 때문에 대부분 외부에 열어준 곳을 시작점으로 사용한다. 이런 이유로 public 메서드에만 트랜잭션을 적용하도록 설정되어 있다
profile
진짜 개발자가 되어보자

0개의 댓글