결론부터 말하면 동작하지 않는다
Spring에서는 @Transactional, @Cacheable, @Async 같은 애너테이션이 런타임에 동작하는 Spring AOP 기반으로 처리된다.
Spring AOP는 JDK Dynamic Proxy나 CGLIB 방식을 이용해 프록시 객체를 생성하고, 메서드 호출 전후로 횡단 관심사를 적용한다.
스프링은 빈을 생성할 때, 해당 클래스에 AOP 애너테이션이 존재하는지 검사한 후, 존재하면 프록시 객체로 감싸서 등록한다.
Spring의 기본 프록시 방식 (JDK 동적 프록시 또는 CGLIB)을 사용할 때, private 메서드는 프록시 클래스에 존재하지 않거나 접근할 수 없기 때문에 호출이 불가능
@Transactional이 붙은 메서드는 프록시 객체가 호출할 때만 트랜잭션이 시작
하지만 private 메서드는 프록시가 아닌 자기 자신(this) 에서 직접 호출되기 때문에 프록시가 개입할 수 없다.
@Slf4j
@RequiredArgsConstructor
@Service
public class SelfInvocation {
private final MemberRepository memberRepository;
public void outerSaveWithPublic(Member member) {
saveWithPublic(member);
}
@Transactional
public void saveWithPublic(Member member) {
log.info("call saveWithPublic");
memberRepository.save(member);
throw new RuntimeException("rollback test");
}
public void outerSaveWithPrivate(Member member) {
saveWithPrivate(member);
}
@Transactional
private void saveWithPrivate(Member member) {
log.info("call saveWithPrivate");
memberRepository.save(member);
throw new RuntimeException("rollback test");
}
}
위 코드에서 saveWithPublic 메서드는 public 접근 제어자이기 때문에 프록시를 통해 트랜잭션이 적용된다.
반면 saveWithPrivate 메서드는 private 접근 제어자이기 때문에 트랜잭션이 적용되지 않는다.
또한, 같은 클래스 내에서 메서드를 호출하는 경우(Sefl Invocation)에는 프록시를 거치지 않고 직접 호출하게 되어 트랜잭션 어드바이스가 적용되지 않는다.
테스트 결과 요약
외부에서 selfInvocation.saveWithPublic()을 직접 호출하면 트랜잭션이 정상 동작하고 롤백된다.
outerSaveWithPublic()처럼 내부 메서드를 통해 호출하면 트랜잭션이 적용되지 않아 롤백이 되지 않는다.
private 메서드는 애초에 AOP 적용 대상이 아니기 때문에 트랜잭션이 적용되지 않는다.