@SpringBootTest
public class InternalCallV1Test {
@Autowired
CallService callService;
@Test
void printProxy() {
log.info("callService class={}", callService.getClass());
}
@Test
void internalCall() { // 정상 실행됨!
callService.internal();
}
@Test
void externalCall() { // external() 내부의 inernal()이 트랜잭션 적용을 받지 못함!!
callService.external();
}
@TestConfiguration
static class InternalCallV1Config {
@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);
}
}
}
@Transational 메서드가 적용되지 않는 메서드(A 메서드)에서, 외부 메서드(@Transactional가 붙은 B 메서드)를 호출하면 안 된다.
왜냐하면, B 메서드는 프록시 객체를 통해서만 호출이 된다.
만약 A 메서드를 실행을 하게 되면, A 메서드는 프록시 객체를 거치지 않고, 실제 객체를 직접 참조하여 실행을 하게 된다.
그렇게 되면, A 메서드 내부의 Transaction이 적용되야 하는 B 메서드가 프록시 객체를 통해서 실행되지 않기 때문에
트랜잭션이 적용되지 않는 심각한 문제가 발생을 한다.
Q. @Transactional이 붙지 않은 메서드는 왜 실제 객체를 직접 참조하여 실행을 하는가??
A. 스프링 트랜잭션 AOP는 실제 객체에서 @Transational이 붙어 있는 메서드만을 오버 라이딩을 하여 구현된다.
고로, 애노테이션이 없는 메서드의 경우, 프록시를 통해서 호출을 할 수가 없기 때문이다.
반대로, @Transactional이 붙어 있는 메서드는 무조건 프록시만을 통해서 트랜잭션 적용을 받는다.
위 사진에도 나와 있듯이, internal() 호출 시, this(실제 객체)로 호출이 되고 있다.
위에서도 언급을 하였듯이, 스프링은 프록시를 통해서만 트랜잭션을 적용 받기 때문에 this로 internal()를 호출하게 트랜잭
션이 적용되지 않는다.
해결 방법 : 내부 호출을 피하기 위해, internal() 메서드를 별도의 클래스로 분리!!
( 트랜잭션 AOP 주의 사항 - 프록시 내부 호출2 참조!)
'CS 잡지식' 카테고리의 다른 글
동기화(Synchronization) VS 동시성(Concurrency) (0) | 2023.02.18 |
---|---|
HTML vs CSS (0) | 2023.02.18 |
HTML, CSS, Java Script에 대한 고찰! (0) | 2023.02.18 |
예외는 2가지 측면에서 생각! (0) | 2023.02.13 |
SQL 작성 TIP (0) | 2022.12.08 |