단서해
개발일지
단서해
전체 방문자
오늘
어제
  • 분류 전체보기 (29)
    • Project (5)
    • JAVA (2)
    • Server (11)
      • Info (4)
      • 작동 원리 예상 (4)
      • Question (1)
      • JPA (1)
    • Infra (2)
    • Trouble Shooting (8)

블로그 메뉴

  • 홈
  • 글쓰기
  • 관리

공지사항

인기 글

태그

  • Github Actions
  • Java
  • @configuration
  • REQUIRES_NEW
  • @Component scan
  • gradle-wrapper.properties
  • 파일 경로
  • enhanced for
  • Intellij
  • 분산락
  • 동시성
  • 비동기
  • CodeDeploy
  • OAuth2.0
  • flutter
  • pinpoint
  • 트랜잭션
  • Reflection
  • 무중단 배포
  • yml
  • thymeleaf
  • gradle
  • Lettuce
  • Spring
  • 성능 개선
  • Fileter
  • gradlew
  • JPA
  • error
  • docker

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
단서해

개발일지

Custom Advisor 에 @Transactional 설정시 미적용 문제
Server/Info

Custom Advisor 에 @Transactional 설정시 미적용 문제

2023. 3. 20. 18:37

@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
    'Server/Info' 카테고리의 다른 글
    • [Lombok] @Builder 로 객체 생성시, 초기화 안해준 필드 null 값으로 세팅
    • [Spring] Component Scan 범위
    • [Spring] @Configuration 을 설정해줘야 하는 이유.
    단서해
    단서해

    티스토리툴바