Spring 트랜잭션 AOP 주의 사항

MinSeong Kang·2022년 8월 7일
0

spring

목록 보기
11/18

아직 Spring으로 공부하거나 개발을 하면서 해당 트랜잭션 AOP 문제를 마주한 적은 없다. 하지만 이 문제는 실무에서 많이 마주친다고 강조하셔서, 그 때를 대비에 포스팅하려고 한다.

@Transactional

먼저 @Transactional을 사용하면 프록시 방식의 AOP인 스프링 트랜잭션 AOP가 적용된다. 따라서 프록시 객체가 요청을 먼저 받고 트랜잭션을 처리하고 실제 객체를 호출한다. 만일 프록시를 거치지 않고 대상 객체를 직접 호출하면 AOP가 적용되지 않아 트랜잭션 또한 적용되지 않는다.

@Slf4j
class CallService {
        public void f1() {
            log.info("call f1");
            printTxInfo();
        }

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

        public void printTxInfo() {
            boolean txActive 
            	= TransactionSynchronizationManager.isActualTransactionActive();
            log.info("tx active = {}", txActive);
        }
    }
  • f1() : 트랜잭션이 적용되지 않는다.
  • f2() : @Transactional을 통해 트랜잭션이 적용한다.
  • f3() : 트랜잭션을 적용하지 않고, f2()을 내부적으로 호출한다.

테스트

    @Test
    void F1Call() {
        callService.f1();
    }

트랜잭션 적용 X

    @Test
    void F2Call() {
        callService.f2();
    }

트랜잭션 적용 O

    @Test
    void F3Call() {
        callService.f3();
    }

f3() f2() 모두 트랜잭션 적용 X ➝ 메서드 내부 호출

자바에서 메서드 앞에 별도의 참조가 없으면 this라는 뜻으로 자기 자신의 인스턴스를 가르킨다. 따라서 자기 자신의 내부 메서드를 호출하는 this.f2()가 되는데, 이런 내부 호출은 프록시를 거치지 않는다. 따라서 프록시를 적용할 수 없다.

@Transactional을 사용하는 트랜잭션 AOP는 프록시를 사용하여, 프록시를 사용시 메서드 내부 호출에 프록시를 적용할 수 없다.!!!! ➝ 프록시 방식으 AOP 한계


해결 방안

f2() 메서드를 별도의 클래스로 분리!

 @Slf4j
    @RequiredArgsConstructor
    static class F3Service {

        private final F2Service internalService;

        public void f3() {
            log.info("call f3");
            printTxInfo();
            internalService.f2();
        }

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

    @Slf4j
    static class F2Service {
        @Transactional
        public void f2() {
            log.info("call f2");
            printTxInfo();
        }

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

f2()에 트랜잭션이 잘 적용되는 것을 확인할 수 있다.


참고문헌

https://www.inflearn.com/course/스프링-db-2/dashboard

0개의 댓글