스프링/6. 스프링 DB-2

42. 트랜잭션 AOP 주의사항 -프록시 내부 호출

sdafdq 2023. 10. 14. 21:45

트랜잭션이 제대로 적용 안되는 문제,

그로 인하여 default인 오토커밋모드로 커밋되어 rollback을 해도 의도되로 안되는..

 

우선 다음 코드를 보자.

@Slf4j
@SpringBootTest
public class InternalCallV1Test {

    @Autowired
    CallService callService;

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

    @Test
    void internalCall(){
        callService.internal();
    }

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


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

    static class CallService{
        public void external(){
            log.info("call external");
            internal();
        }

        @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);
        }
    }
}

externalCall()을 보면,

CallService의 external을 호출한다.

 

근데 external은 @Transactional이 아니며, 

코드 내부에서는 @Transactional인 internal()을 호출하지만, 트랜잭션은 어느 곳에서도 시작되지 않는다.

 

그 이유는, @Transactional, 아니 프록시의 가로채는 방식에 있다.

 

@Test랑 똑같다. 그냥 그런 식으로 사용되도록 만들어 진 것이다.

@BeforeEach
public void before(){
    log.info("before");
}

@Test
void check(){
    proxyCheck();
    proxyCheck();
}

@Test
void proxyCheck(){
    log.info("aop class={}", basicService.getClass());
    Assertions.assertThat(AopUtils.isAopProxy(basicService)).isTrue();
}

test에 이런 코드를 짜 봤는데,

check() 에서

@Test가 붙은 proxyCheck()를 2번이나 호출했지만,

before()은 한번만 호출되었다.

 

원래 @BeforeEach라는 것이, @Test 하기 전에 무조건 실행되는 건데, 

@Test가 붙은 proxyCheck()를 2번이나 호출했지만 한번밖에 실행되지 않았다.

원래대로라면 @Test check() 한번, 그 후 proxyCheck() 두번 해서 총 3번 실행 되어야 한다.

 

하지만 한번만 호출 되는 것이 우리가 의도한 코드 그대로가 맞다는 것이다.

 

즉, 프록시도 저러 한 이유일 것이다.

그렇게 동작하도록 설계가 되었을 것이다.

 

 

프록시는 원래클래스를 상속받아 만든 것 이기 때문에, 원래 함수들을 다 자기 함수로 인식한다.