기술 면접에서 잘 답변하지 못한 깊은 부분을 공부하며 정리한다.
Spring AOP 의 원리와 개념들을 공부한다.
// Aspect: 흩어진 관심사를 모듈화한 것 (여기서는 로그 기록 기능)
@Aspect
public class LoggingAspect {
// PointCut: 특정 JointPoint를 선정하는 기준 (여기서는 "placeOrder()" 메서드 실행 전)
@Pointcut("execution(* com.example.service.OrderService.placeOrder(..))")
private void placeOrderMethod() {}
// Advice: PointCut에 지정된 JointPoint에서 실행될 실제 부가기능 (여기서는 로그 출력)
@Before("placeOrderMethod()")
public void logBefore(JoinPoint joinPoint) {
// JointPoint: Advice가 적용되는 지점 (여기서는 "placeOrder()" 메서드 실행 전)
System.out.println("Executing method: " + joinPoint.getSignature().getName());
}
}
// Target: Aspect를 적용하는 곳 (여기서는 OrderService 클래스)
public class OrderService {
// 이 메서드가 Target이며, PointCut에 의해 선택된 JointPoint에서 Aspect가 적용됩니다.
public void placeOrder() {
System.out.println("Order placed successfully!");
}
}
기본적인 원리는 Proxy를 활용해서 동작한다. 다만 이 Proxy를 등록시키는 방법이 두가지가 있다.
Dynamic Proxy 기술은 JDK 1.3 버전 부터 들어간 기술이며, java reflection으로 동작한다.Dynamic Proxy의 경우, 클래스에는 적용할 수 없고, 인터페이스를 대상으로만 사용할 수 있습니다.
당연하게도, final 클래스나 final 메서드의 경우 상속 시 문제가 생기므로 이 방식을 적용할 수 없습니다.
나는 컴파일 시점에 적용된다고 착각하였으나, BeanPostProcessor에서 런타임에 바이트 코드를 생성하는 방식으로 작동한다.
AOP 프록시의 경우 빈 생명 주기 중, 콜백 호출 부분에 해당하는 BeanPostProcessor에서 생성되게 된다.

이 과정에서 프록시가 적용된다.
기본적으로는 어떤 프록시가 사용될까?
Spring Boot 의 경우 기본 옵션으로 테스트 해보면, 전부 CG Lib 프록시가 생성되는 것을 볼 수 있는데 그 이유는 Spring Boot 의 자동설정 때문이다.
TransactionAutoConfiguration 를 보면 CG Lib을 기본적으로 적용하도록 설정이 되어 있다.
Dynamic Proxy 를 통해 프록시를 생성하면, 세부 구현체가 감춰진다는 점을 주의해야 한다.
이 때문에 이 방식을 사용할 때는, DI 시 반드시 인터페이스를 통해 주입받도록 구현해야 한다. 그렇지 않다면, 기존에는 잘 작동하던 코드가 AOP 적용 후 주입이 불가능하다며 에러를 낼 것이다.
리플렉션 특성상 느리다.
이 방식을 사용할 때는, CG Lib에 대한 의존성을 추가해야한다. 그리고 내가 확인하기로는 Java 17+ 에 대한 지원을 종료하였다.
https://github.com/spring-projects/spring-framework/issues/12840
이 이슈를 보면, spring 5.0부터 cglib의 대안인 byte buddy 프록시가 추가되었다.