waveofmymind
기록하는 습관
waveofmymind
전체 방문자
오늘
어제
  • 분류 전체보기 (124)
    • 📝 정리 (5)
    • 🌊TIL (9)
    • 💻CS (1)
      • 자료구조 (1)
    • 📙Language (9)
      • ☕Java (6)
      • 🤖Kotlin (3)
    • 🍃Spring (28)
    • 👨🏻‍💻알고리즘 (67)
      • 프로그래머스 (59)
      • 백준 (3)
    • 👷DevOps (4)
      • 🐳Docker (2)
      • 🤵Jenkins (1)

블로그 메뉴

  • 홈
  • Spring
  • Java
  • 알고리즘

공지사항

인기 글

태그

  • kotest
  • chat GPT
  • CORS
  • 스택
  • 스프링 시큐리티
  • spring
  • sql
  • JDBC
  • kotlin
  • resultset
  • spring boot
  • 트랜잭션 전파
  • 코틀린
  • LeetCode
  • AOP
  • til
  • Connection
  • BFS
  • 완전탐색
  • SpringAOP
  • Spring Security
  • 스프링 부트
  • 트랜잭션
  • 다이나믹 프로그래밍
  • 챗GPT
  • mybatis
  • 통합테스트
  • 스프링
  • Open AI
  • 힙

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
waveofmymind

기록하는 습관

[Spring] 트랜잭션 - 프록시 내부 호출 문제 1
🍃Spring

[Spring] 트랜잭션 - 프록시 내부 호출 문제 1

2023. 2. 3. 10:02

@Transactional을 사용하면 스프링의 트랜잭션 AOP가 적용된다.

이는 프록시 방식의 AOP를 사용하는데, 프록시 객체를 스프링 빈으로 주입받아 메서드가 실행되면, 적용 대상인지 아닌지를 프록시 객체가 판단 후에 실제 객체의 메서드를 호출하는 것이다.

만약 대상 객체를 직접 호출하게 되면 AOP가 적용되지 않고, 트랜잭션도 적용되지 않는다.

대상 객체 내부에서 메서드 호출이 발생할 경우, 프록시를 거치지 않고 대상 객체를 직접 호출하는 문제가 발생 할 수 있다.

이 경우 @Transactional이 있어도 트랜잭션이 적용되지 않는다.

 

InternalCallV1Test

@Slf4j
@SpringBootTest
public class InternalCallV1Test {
	@Autowired CallService callService;

    @Test
    void printProxy() {
        log.info("callService class={}",callService.getClass());
    }
	@Test
    void internalCall() {
        callService.internal(); }
	@Test
    void externalCall() {
        callService.external(); }
	
    @TestConfiguration
    static class InternalCallV1TestConfig {
        @Bean
        CallService callService() {
            return new CallService();}
    }
    @Slf4j
    static class CallService {
        public void external() {
            log.info("call external");
            printTxInfo();
            internal();
        }
        @Transactional
        public void internal() {
            log.info("call internal");
            printTxInfo();
        }
        private  void printTxInfo() {
            boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
            log.info("tx active={}",txActive);
            boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
            log.info("tx readOnly={}",readOnly);
        }
    }
}

callService에서 external 메서드는 트랜잭션을 선언하지 않았고, internal()은 트랜잭션을 선언했다.

따라서 external 메서드를 실행할 경우, 트랜잭션이 시작하지 않을 것이고, 내부에서 트랜잭션을 선언했던 internal을 호출할 경우 어떻게 될까?

  • internal() 메서드 실행

  • external() 메서드 실행

예상과 다르게 external에서 트랜잭션을 선언했던 internal 메서드를 실행했음에도 불구하고 트랜잭션이 시작되지 않았다. 

문제 원인

external 메서드가 실행되는 호출의 흐름은 아래와 같다.

  1. callService.external() 호출
  2. callService의 트랜잭션 프록시 호출
  3. external()은 트랜잭션 적용 대상이 아니므로 실제 객체의 external() 호출
  4. external() 내부에서 internal() 메서드 호출

자바에서는 메서드 앞에 참조 객체를 생략할 경우, this라는 뜻으로 자신의 인스턴스의 메서드를 호출한다.

자신의 인스턴스는 실제 객체이므로, 트랜잭션 프록시를 거치지 않아서 트랜잭션이 적용되지 않는 것이다.

 

이 문제를 해결하기 위해 internal() 메서드를 별도의 클래스로 분리해서 사용해야 한다.

출처

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard

 

스프링 DB 2편 - 데이터 접근 활용 기술 - 인프런 | 강의

백엔드 개발에 필요한 DB 데이터 접근 기술을 활용하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., - 강의 소개 | 인

www.inflearn.com

 

 

'🍃Spring' 카테고리의 다른 글

[Spring] 트랜잭션 전파 - 1  (0) 2023.02.05
[Spring] 트랜잭션 - 프록시 내부 호출 문제 2  (0) 2023.02.03
[Spring] 선언적 트랜잭션 동작 확인하기  (0) 2023.02.03
[Spring DB] MyBatis 사용하기  (0) 2023.01.29
[Spring DB] 테스트 - 트랜잭션  (0) 2023.01.29
    '🍃Spring' 카테고리의 다른 글
    • [Spring] 트랜잭션 전파 - 1
    • [Spring] 트랜잭션 - 프록시 내부 호출 문제 2
    • [Spring] 선언적 트랜잭션 동작 확인하기
    • [Spring DB] MyBatis 사용하기
    waveofmymind
    waveofmymind

    티스토리툴바