A 라는 Service 객체의 메서드가 존재하고 내부에서 로컬 트랜잭션이 3개가 존재한다고 할 때, @Transactional을 A 메서드에 적용하였을 때 어떠한 일이 벌어지고, 어떤 요청 흐름이 발생하게 되나요?
어플리케이션에서는 커넥션풀을 통해서 커넥션 객체를 가져오고, 이 커넥션 객체를 통해서 데이터베이스의 요청을 보냄. 데이터베이스에서는 하나의 세션에 할당해서 하나씩 처리한다.
스프링이 이를 어떻게 처리할까?
스프링에서 많이 사용되는 선언적 트랜잭션 방식이다.
getConnection(), setAutoCommit(false), 예외 발생 시 롤백, 정상 종료 시 커밋 등의 필요한 코드를 삽입해준다.
@Transactional 애너테이션이 있으면, 프록시 객체가 빈으로 등록된다.
스프링이 제공하는 선언적 트랜잭션 관리를 통해, 서비스 레이어에 트랜잭션 관련된 코드 혹은 특정 기술에 종속된 코드를 분리했다.
스프링부트에서는 @EnableTransactionManagement 설정이 되어 있어서 자동으로 사용할 수 있으며, 입맛에 맞게 클래스 또는 메서드에 @Transactional을 사용하면 된다.
PlatformTransactionalManager는 TransactionManager의 최상위 인터페이스로, 환경에 맞는 클래스를 주입할 수 있도록 구성되어 있다.@Transactional 애너테이션이 있으면, 해당 빈을 상속받은 프록시 객체를 생성한다. 따라서 private메서드는 상속이 불가능하기 때문에, 어노테이션을 붙여도 동작하지 않는다.Spring AOP를 통해 프록시 객체를 생성하여 사용한다.
스프링에서 직접 참조하지 않고, 프록시 객체를 사용하는 이유는 Aspect클래스에서 제공하는 부가기능을 사용하기 위해서이다. 직접 참조하는 경우는 원하는 위치에서 직접 Aspect클래스를 호출해야하기 때문에 유지보수가 어려워진다.
프록시를 통해 실제 객체를 호출하여 트랜잭션을 처리하는 도중 RumtimeException이 발생하면 처리를 종료하고 rollback을 수행하게 됩니다. rollbackFor 옵션을 주어 처리하는 방법도 있다.

org.springframework.transaction.interceptor에서 낚아채서
invokeWithinTransaction이란 메서드가 동작하게됨.
트랜잭션 시작 -> 타겟 메서드 실행 -> 트랜잭션 종료
try문 안에서 invocation.proceedWithInvocation()이 타겟객체를 직접 호출
commitTransactionAfterReturning(txInfo) 동작들이 정상적으로 수행되었을 때, 커밋을 수행한다.
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
...
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager cpptm)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
...
트랜잭션은 커밋이나 롤백등의 어떤 메서드가 호출될지 모르기에, Advise메서드를 지정할수 없고, aspect가 아닌 advisor로 aop를 설정해야 한다.
참고
org.springframework.transaction.interceptor.TransactionAspectSupport에 있는 invokeWithinTransaction()을 사용하고 있는데,
이것이 Aspect를 사용하는 것처럼 보일 수 있지만, 실제로는 Advisor를 통해 구현된 것이다.
'TransactionInterceptor' 자체가 'MethodInterceptor'를 구현하는 Advisor의 일종이기 때문이다. 'TransactionInterceptor'는 'invoke()' 메서드를 통해 트랜잭션을 관리하며, 이는 'invokeWithinTransaction()' 메서드를 호출하는 방식으로 동작한다.
메서드 별로 다른 트랜잭션을 적용하려면 곧 어드바이스의 기능을 확장해야 함.
스프링에선 메서드 패턴에 따라 경계설정을 할 수 있도록 TransactionInterceptor를 제공함.
선언적 트랜잭션 관리 - @Transactional 대부분의 경우에 충분함.
@Transactional 애너테이션이 있으면, Spring AOP를 통해 프록시 객체를 생성하여 사용한다.
Transaction Advisor에게 전달한다.
Transaction Advisor는 트랜잭션을 생성한다.
Transaction Advisor는 커밋 또는 롤백 등의 트랜잭션 결과를 반환한다
invocation.proceedWithInvocation()이 타겟객체를 직접 호출
commitTransactionAfterReturning(txInfo) 동작들이 정상적으로 수행되었을 때, 커밋을 수행
타겟객체를 직접 호출한다
https://www.youtube.com/watch?v=taAp_u83MwA
https://sasca37.tistory.com/267
찾아볼것.
트랜잭션 매니저
트랜잭션 동기화 매니저
AOP