250x250
Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
Tags
- 김영한
- 프록시
- 코딩테스트
- 티스토리챌린지
- 인터페이스
- 프로그래머스
- 코드트리조별과제
- DI
- 오블완
- 코드트리
- java
- 다익스트라
- equals()
- 테스트코드
- 예외와 트랜잭션 커밋
- 백준
- 오버라이딩
- @configuration
- html form
- objecterror
- http 메시지 컨버터
- 의존관계
- 싱글톤
- 스프링
- 참조변수
- 추상클래스
- HttpServletResponse
- fielderror
- 서블릿
- 스프링컨테이너
Archives
- Today
- Total
minOS
트랜잭션 AOP 주의사항 - 프록시 내부 호출 본문
728x90
프록시 내부 호출 문제 예시
package com.example.springtx.apply; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronizationManager; @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(); } } static class CallService{ public void external(){ log.info("call external"); this.printInfo(); this.internal(); // this(생략) 나 자신의 인스턴스 호출 --> 내부 호출은 프록시를 무시함. } @Transactional public void internal(){ log.info("call internal"); printInfo(); } private void printInfo(){ boolean txActive = TransactionSynchronizationManager.isActualTransactionActive(); log.info("tx active={}",txActive); } } } // 프록시 방식의 AOP 한계 //@Transactional을 사용하는 트랙잭션 AOP는 프록시를 사용한다. 프록시를 사용하면 메서드 내부 호출에 프록시를 적용할 수 없다. //해결 - > 내부 호출을 피하기 위해 internall() 메서드를 별도의 클래스로 분리
1) 트랜잭션 정상 작동
@Test void internalCall(){ callService.internal(); }
2) 내부 호출 시 결과@Test void externalCall(){ callService.external(); }
internal 메서드 트랜잭션 무시된 것을 알 수 있음
프록시 내부 호출 문제 해결
package com.example.springtx.apply; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronizationManager; @Slf4j @SpringBootTest public class InternalCallV2Test { @Autowired CallService callService; @Test void printProxy(){ log.info("callService class={}",callService.getClass()); } @Test void internalCall(){ callService.internal(); } @Test void externalCall2(){ callService.external(); } @TestConfiguration static class InternalCallV1TestConfig{ @Bean CallService callService(){ return new CallService(internalService()); } @Bean InternalService internalService(){ return new InternalService(); } } @Slf4j @RequiredArgsConstructor static class CallService{ private final InternalService internalService; public void external(){ log.info("call external"); this.printInfo(); internalService.internal(); // this(생략) 나 자신의 인스턴스 호출 --> 내부 호출은 프록시를 무시함. } @Transactional public void internal(){ log.info("call internal"); printInfo(); } private void printInfo(){ boolean txActive = TransactionSynchronizationManager.isActualTransactionActive(); log.info("tx active={}",txActive); } } static class InternalService{ @Transactional public void internal(){ log.info("call internal"); printInfo(); } private void printInfo(){ boolean txActive = TransactionSynchronizationManager.isActualTransactionActive(); log.info("tx active={}",txActive); } } }
실제 호출되는 흐름
1. 클라이언트인 테스트 코드는 `callService.external()` 을 호출한다.
2.`callService` 는 실제 `callService` 객체 인스턴스이다.
3. `callService` 는 주입 받은 `internalService.internal()` 을 호출한다.
4. `internalService` 는 트랜잭션 프록시이다. `internal()` 메서드에 `@Transactional` 이 붙어 있으므로 트랜잭션 프록시는 트랜잭션을 적용한다.
5. 트랜잭션 적용 후 실제 `internalService` 객체 인스턴스의 `internal()` 을 호출한다.
728x90
'TIL > 김영한의 스프링 DB 2편' 카테고리의 다른 글
| 예외와 트랜잭션 커밋,롤백-활용 (0) | 2025.03.03 |
|---|---|
| 예외와 트랜잭션 커밋,롤백-기본 (0) | 2025.03.03 |


