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