public class Exam() {
public void A () {
~~~
B ();
~~~
}
@Transactional
public void B () {
~~~
}
}
위와같이 한 클래스 내에 A, B 2개의 메소드를 작성했고,
B에 트랜잭션 어노테이션을 붙인후에 A의 내부에서 호출되도록 했다.
저렇게 작성한 이유는
분산락을 이용해 동시성 문제를 해결해보고자하는 과정에서
분산환경에서의 프로세스를 아래와 같은 순서로 진행하게끔 하고싶어서였다.
락 획득 > 트랜잭션 실행 > 비즈니스로직 > 트랜잭션 커밋 > 락 해제
하지만 무지성으로 트랜잭션이 걸려있는 메소드를 분산락을 가진 분기의 내부로 넣어봤을때 테스트는 올바르게 실행되지 않았다..
Spring의 AOP (Aspect Oriented Programing) 관점지향형 프로그래밍
- 반복사용되는 로직들을 모듈화 하여 필요할때 호출해서 사용하는 방법
- 다이나믹 프록시 혹은 CGLib(Spring boot, JPA hibernate default)로 생성된 proxy 객체를 사용
proxy?
어떤 객체를 사용하고자 함
-> 객체를 직접적으로 참조하는것이 아닌 해당 객체를 대행하는 객체(proxy)를 이용해 대상 객체에 접근
Transactional은 대표적인 AOP가 적용되는 사례.
AOP에서 proxy의 동작과정
proxy를 통해 들어오는 외부 호출을 인터셉트해 작동하게됨
위의 이슈케이스의 경우
exam class의 A 호출
-> proxy! A호출해줘! ( proxy.A(); )
-> proxy 내부의 A(), B() 에서 A()가 호출됨
-> A() 로직에서 proxy내부의 B()를 호출하게됨 = self-invocation
@Transactional 어노테이션은 proxy외부에서 접근해야 AOP가 적용됨 !
public class Exam() {
public void A () {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
~~~
try {
B ();
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
~~~
}
public void B () {
~~~
}
}
@Service
public class ExamB {
@Transactional
public void B() {
~~~
}
}
@Component
@RequiredArgsConstructor
public class Exam {
private final ExamB examB;
public void A() {
~~~
examB.B(); // B 메소드 호출
~~~
}
}