프로젝트를 진행하던 중 해당 서비스 내에서만 사용하는 메소드에 private을 적용하려고 보니 intellij에서 아래와 같이 빨간줄이 표시되었다.
그리고 아래와 같은 오류메시지를 알려줬다.
Methods annotated with '@Transactional' must be overridable.
문제를 해결하려고 검색을 하다 보니 @Transactinoal의 동작방식에 대해 좀 알게 되었다.
@Transactional은 spring AOP를 사용하여 구현되는데 기본적으로 AOP는 proxy패턴을 사용하여 구현이 된다. 이를 확인하기 위해 debug 모드로 OrderService에 주입된 객체를 확인해보았고 아래와 같이 실제 객체와 다른 객체가 들어가 있음을 확인했다.
그렇다면 proxy는 어떻게 동작을 하길래 private 메소드가 먹히지 않는 걸까?
우선 아래에서 프록시가 어떠한 매커니즘으로 동작하는지 확인해보자.
아래는 내가 실행하려는 Serivce와 그 메소드이다.
@Service
@AllArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderRepository orderRepo;
@Override
@Transactional
public Order makeOrderWtiTrans(Long itemId, int quantity) throws Exception {
return new Order(itemId, quantity);
}
}
그리고 실제 spring이 만들어 리턴해준 proxy가 먹힌 서비스의 동작방식은 아래와 같은 매커니즘으로 동작을 할것이라고 추측되는 소스는 아래와 같다.
public class OrderServiceProxy {
private final OrderService orderService;
private final TransactonManager manager = TransactionManager.getInstance();
public OrderServiceProxy (OrderService orderService) {
this.orderService = orderService;
}
public void makeOrderWtiTrans(Long itemId, int quantity) {
try {
manager.begin();
boardService.makeOrderWtiTrans(Long itemId, int quantity);
manager.commit();
} catch ( Exception e ) {
manager.rollback();
}
}
}
위 코드를 보면 OrderServiceProxy에서 실제 내가 구현한 OrderService를 가져와서 makeOrderWtiTrans를 호출하는데 private을 사용하면 해당 메소드를 호출할 수가 없으므로 private이 불가했던 것이다.
결론
1. spring의 @Transactional은 proxy로 동작한다.
2. 그러므로 private 사용이 불가하다.
추가로 알아둬야할 사항.. spring aop의 proxy 구현방식에도 종류가 있더라 하나는 JDK Proxy(Dynamic Proxy라고도 부름)와 다른 하나는 CGLib이다. 다음에 이거에 대해서 더 자세하게 써보겠다
https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html