@Transactional은 Spring AOP 를 사용하여 동작한다.
그럼 만약 아래와 같은 상황처럼 Custom Advisor[customAdvisor] 와 해당 Adivsor를 적용하려는 메서드[test] 에 @Transactional 설정을 아래와 같이 했을 경우 어떤 구조로 동작할까?
@Service
public class TestService {
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
public void test() {
log.info("Test");
}
}
@Aspect
@Component
public class AspectConfig {
private final TestJpaRepository testJpaRepository;
@Transactional(readOnly = false)
@Around("execution(* com..*Service.*(..))")
public Object customAdvisor(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
Parent parent = new Parent();
parent.setJob("sales");
testJpaRepository.save(parent);
return result;
}
}
예상 및 실제 동작
처음에 내가 생각한 메서드콜 플로우는 다음과 같다
💡 Call → AOP Proxy → Custom Advisor → Transaction Proxy → Transaction Advisor → Target Method
따라서 처음에 customAdvisor()가 호출된 이후 joinPoint.proceed()[=test()] 를 호출하고, test() 실행 동안에는 Propagation.REQUIRES_NEW 속성 설정을 해당 메서드에 하였기 때문에 readOnly = true 인 새로운 트랜잭션이 시작된다고 생각했다.
이후 test() 가 끝난 뒤 customAdvisor() 로 돌아오면 기존의 readOnly = false 인 트랜잭션 환경에서 정상적으로 testJpaRepository.save(parent) 가 동작할 것이라고 예상했다.
하지만 예상과는 다르게 testJpaRepository.save(parent) 호출시 트랜잭션의 readOnly가 true로 설정되어 있어 해당 작업이 불가능하다는 오류가 발생하였다
그래서 현재 트랜잭션의 readOnly 설정이 무엇인지 알려주는 메서드인TransactionSynchronizationManager.isCurrentTransactionReadOnly() 를 통해 확인해 본 결과, customAdvisor() 호출시에 이미 readOnly = true 인 트랜잭션이 적용되어 있다는 것을 알아내었다
실제 동작 결과의 원인
나는 원래 알고 있던 메서드콜 플로우 대로 라면 나올 수 없는 결과임을 인지하였고, 실제 플로우는 어떻게 흘러가는지에 대해 다음 자료를 통해 이해하였다
먼저 AOP Proxy와 Transaction Proxy 가 각각 한 개씩 생성된다고 잘못 생각하고 있었다. 실제로는 하나의 프록시가 생성되고, 그 안에서 Transaction Advisor와 Custom Advisor가 연쇄적으로 적용되는 형태였다.
그리고 원하는대로 트랜잭션이 적용되지 않았던 가장 근본적인 이유는 customAdvisor() 에 설정한 @Transactional 이 작동되지 않아서였다.
따라서 test() 에 설정한 @Transactional 관련 설정들이 customAdvisor() 실행시에도 그대로 이어졌고 앞서 말한 결과가 나온 것 이었다.
결과적으로 내가 원했던대로 save 가 정상 작동하게 하기 위해서, save 메서드 자체에 아래와 같은 설정을 하였다.
public interface TestJpaRepository extends JpaRepository<TestLog, Long> {
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = flase)
TestLog save(TestLog testLog);
}
내부 동작 예상
Spring AOP 설정시 메서드 기준으로 aop가 작동하기 때문에 트랜잭션 설정 또한 해당 메서드에 적용된
@Transactional 설정 기준으로 Transaction Advisor가 생성되는 걸로 예상된다.
'Server > Info' 카테고리의 다른 글
[Lombok] @Builder 로 객체 생성시, 초기화 안해준 필드 null 값으로 세팅 (0) | 2022.11.16 |
---|---|
[Spring] Component Scan 범위 (0) | 2022.07.29 |
[Spring] @Configuration 을 설정해줘야 하는 이유. (0) | 2022.07.29 |