새소식

반응형
Spring/etc

[Spring] @Transactional 동작방식 및 주의사항

  • -
반응형

@Transactional은 Proxy 방식으로 동작한다

간략하게 어떻게 proxy가 생성되는지 알아보자

@Service
@RequiredArgsConstructor
public class TestService {

    public void insertTest(){
        // do something
    }
}
public class TestServiceProxy{
    private final TransactonManager tm = TransactionManager.getInstance();

    public void insertTest() {
        try {
           tm.begin(); // 트랜잭션 전처리(트랜잭션 시작, autoCommit(false) 등)
           insertTest(); // 기존 로직
           tm.commit(); // 트랜잭션 후처리(트랜잭션 커밋 등)
        } catch ( Exception e ) {
            tm.rollback(); // 트랜잭션 오류 발생 시 롤백
        }
    }

 

위 메서드에 대한 호출이 들어왔을 때 AOP proxy가 이를 가로채서 위와 같은 형태로 실행을 시키도록 해준다

 

스프링 AOP에서 Proxy는 크게 JDK Dynamic proxy 또는 CGLIB으로 작동합니다

스프링 부트 1.4 버전 이후부터는 default로 CGLIB을 사용합니다.

 

JDK Dynamic proxy 

  • Proxy 객체를 생성할 때 내부적으로 reflection을 사용
    • 비용이 비싸다
  • 타겟 클래스의 인터페이스를 Proxy 객체로 구현해서 코드를 끼워 넣음
    • 인터페이스 구현이 필수

CGLIB

  • 상속, 바이트코드 조작을 통해 Proxy 객체를 생성 

 

Proxy 방식을 이용하기 때문에 생기는 주의점

 

private는 @Transactional이 적용되지 않는다

위에서 언급한 것처럼 상속을 통해서 Proxy 객체가 생성되므로 private으로 선언한 메서드는 Proxy에서 접근할 수 없어 transactional 기능을 이용불가능 하다

 

같은 클래스 내의 메서드를 호출하면 트랜잭션이 적용되지 않는다 (self-invocation)

외부에서 스프링을 통해 주입받은 객체를 활용하여 메서드 호출을 해야 proxy가 적용된 메서드 호출이 되게 된다

 

아래의 예시에서 TestA의 init 메서드에서 호출한 progress는 트랜잭션을 적용해 줄 수 없다

public class TestA {
    public void init() {
        this.progress();
    }

    @Transactional
    public void progress() {
        // do somethings .. 
    }
}

public class TestB {
    @AutoWired
    TestA test;
    
    public void v1() {
        test.init(); // no transaction in progress 
    }

}

 

만약, TestB에서 testA.init을 호출하면 트랜잭션이 잘 적용될 것이다. 

public class TestB {
    @AutoWired
    TestA test;
    
    public void v1() {
        test.progress();
    }

}

출처 : https://kdhyo98.tistory.com/53

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.