728x90
프로젝트 생성

dependencies {
...
//테스트에서 lombok 사용
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
}
- test에서도 Lombok을 사용하기 위해 의존성 추가
트랜잭션 적용 확인
@Slf4j
@SpringBootTest
public class TxBasicTest {
@Autowired BasicService basicService;
@Test
public void proxyCheck(){
log.info("aop class={}", basicService.getClass());
assertThat(AopUtils.isAopProxy(basicService)).isTrue();
}
@Test
public void txTest(){
basicService.tx();
basicService.nonTx();
}
@TestConfiguration
static class TxApplyBasicConfig{
@Bean
public BasicService basicService(){
return new BasicService();
}
}
@Slf4j
static class BasicService{
@Transactional
public void tx(){
log.info("call tx");
boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active={}", txActive);
}
public void nonTx(){
log.info("call none tx");
boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active={}", txActive);
}
}
}
proxyCheck()
- AopUtils.isAopProxy()
- 선언적 트랜잭션 방식에서 스프링 트랜잭션은 AOP를 기반으로 동작한다. @Transactional을 메서드나 클래스에 붙이면 해당 객체는 트랜잭션 AOP 적용의 대상이 되고, 결과적으로 실제 객체 대신에 트랜잭션을 처리해주는 프록시 객체가 스프링 빈에 등록된다. 그리고 주입을 받을 때도 실제 객체 대신에 프록시 객체가 주입된다.
- 클래스 이름을 출력해보면 basicService$$EnhancerBySpringCGLIB... 라고 프록시 클래스의 이름이 출력되는 것을 확인할 수 있다

- @Transactional 애노테이션이 특정 클래스나 메서드에 하나라도 있으면 있으면 트랜잭션 AOP는 프록시를 만들어서 스프링 컨테이너에 등록한다. 그리고 실제 basicService 객체 대신에 프록시인 basicService$$CGLIB를 스프링 빈에 등록한다. 그리고 프록시는 내부에 실제 basicService를 참조하게 된다. 여기서 핵심은 실제 객체 대신에 프록시가 스프링 컨테이너에 등록되었다는 점이다.
- 클라이언트인 txBasicTest 는 스프링 컨테이너에 @Autowired BasicService basicService로 의존관계 주입을 요청한다. 스프링 컨테이너에는 실제 객체 대신에 프록시가 스프링 빈으로 등록되어 있기 때문에 프록시를 주입한다.
- 프록시는 BasicService를 상속해서 만들어지기 때문에 다형성을 활용할 수 있다. 따라서 BasicService 대신에 프록시인 BasicService$$CGLIB를 주입할 수 있다

- 클라이언트가 주입 받은 basicService$$CGLIB는 트랜잭션을 적용하는 프록시이다.
txTest()
로그 추가
logging.level.org.springframework.transaction.interceptor=TRACE
- 이 로그를 추가하면 트랜잭션 프록시가 호출하는 트랜잭션의 시작과 종료를 명확하게 로그로 확인할 수 있다.
basicService.tx()
- 클라이언트가 basicService.tx() 를 호출하면, 프록시의 tx()가 호출된다. 여기서 프록시는 tx() 메서드가 트랜잭션을 사용할 수 있는지 확인해본다. tx() 메서드에는 @Transactional 이 붙어있으므로 트랜잭션 적용 대상이다.
- 따라서 트랜잭션을 시작한 다음에 실제 basicService.tx() 를 호출한다.
- 그리고 실제 basicService.tx() 의 호출이 끝나서 프록시로 제어가(리턴) 돌아오면 프록시는 트랜잭션 로직을 커밋하거나 롤백해서 트랜잭션을 종료한다.
basicService.nonTx()
- 클라이언트가 basicService.nonTx() 를 호출하면, 트랜잭션 프록시의 nonTx()가 호출된다. 여기서 nonTx() 메서드가 트랜잭션을 사용할 수 있는지 확인해본다. nonTx() 에는 @Transactional 이 없으므로 적용 대상이 아니다.
- 따라서 트랜잭션을 시작하지 않고, basicService.nonTx() 를 호출하고 종료한다.
TransactionSynchronizationManager.isActualTransactionActive()
- 현재 쓰레드에 트랜잭션이 적용되어 있는지 확인할 수 있는 기능이다. 결과가 true 면 트랜잭션이 적용되어 있는 것이다. 트랜잭션의 적용 여부를 가장 확실하게 확인할 수 있다.
728x90
'스프링 DB 2편(데이터 접근 활용 기술)' 카테고리의 다른 글
| Ch09. 스프링 트랜잭션 이해 - 트랜잭션 AOP 주의 사항(프록시 내부 호출) (0) | 2022.07.06 |
|---|---|
| Ch09. 스프링 트랜잭션 이해 - 트랜잭션 적용 위치 (0) | 2022.07.06 |
| Ch09. 스프링 트랜잭션 이해 - 스프링 트랜잭션 소개 (0) | 2022.07.06 |
| Ch08. 활용 방안 - 다양한 데이터 접근 기술 조합 (0) | 2022.07.04 |
| Ch08. 활용 방안 - 실용적인 구조 (0) | 2022.07.04 |