728x90
반응형
1. 문제점
스프링은 프록시 방식의 AOP를 사용하는데 이때 AOP를 적용하려면 항상 프록시를 통해서 대상 객체를 호출해야한다. 프록시에서 먼저 어드바이스를 호출하고 이후에 대상 객체를 호출한다. 만약 프록시를 거치지 않고 대상 객체를 직접 호출하게 되면 AOP가 적용되지 않고 어드바이스도 호출되지 않는다.
AOP를 적용하면 스프링은 대상 객체 대신 프록시를 스프링 빈으로 등록한다. 따라서 스프링은 의존관계 주입 시에 항상 프록시 객체를 주입한다. 하지만 대상 객체의 내부에서 메서드 호출이 발생하면 프록시를 거치지 않고 대상 객체를 직접 호출하는 문제가 발생한다.
예시를 통해 알아보자
@Slf4j
@Aspect
public class CallLogAspect {
@Before("execution(* hello.aop.internalcall..*.*(..))")
public void doLog(JoinPoint joinPoint) {
log.info("aop={}", joinPoint.getSignature());
}
}
- aspect 클래스를 먼저 만든다.
@Slf4j
@Component
public class CallServiceV0 {
public void external() {
log.info("call external");
internal();
}
public void internal() {
log.info("call internal");
}
}
- 위의 aspect가 적용되는 클래스이다.
@Import(CallLogAspect.class)
@SpringBootTest
public class CallServiceV0Test {
@Autowired
CallServiceV0 callServiceV0;
@Test
void external() {
callServiceV0.external();
}
@Test
void internal() {
callServiceV0.internal();
}
}
- AOP를 적용할 때 내부 함수를 호출하면 문제가 발생하는 것을 알아보기 위한 테스트 코드이다.
- 이때 external() 함수에서 internal()함수를 내부 호출하고 있는데 AOP가 적용된다면 CallLogAspect 클래스의 doLog()함수 안에서 log가 찍히는 로직이 동작한다.

- external() 실행 시 프록시 호출하면 CallLogAspect 어드바이스가 호출된 것을 확인할 수 있다.
- AOP proxy는 실제 대상(target)의 external()을 호출한다.
- 문제는 external()안에서 internal() 호출 시 발생한다.
- CallLogAspect 어드바이스가 호출되지 않는다.
- 자바 언어에서 메서드 앞에 별도의 참조가 없으면 this라는 뜻으로 자기자신의 인스턴스를 가리킨다.
- 결과적으로 this.internal()이 되는데 여기서 this는 실제 대상 객체(target)의 인스턴스를 뜻한다.
- 내부 호출은 프록시를 거치지 않기 때문에 어드바이스도 적용할 수 없다.
외부에서 internal() 호출하는 경우
- 외부에서 internal() 함수를 호출하는 경우 프록시를 거치기 때문에 CallLogAspect 어드바이스가 적용된 것을 확인할 수 있다.
2. Proxy의 한계
- 프록시 방식의 AOP는 메서드 내부 호출에 프록시를 적용할 수 없다.
- 실제 코드에 AOP를 직접 적용하는 AspectJ를 사용하면 이런 문제가 발생하지 않는다.
- 프록시를 통하는 것이 아니라 해당 코드에 직접 AOP적용 코드가 붙어 있기 때문에 내부 호출과 무관하게 AOP를 적용할 수 있다.
- 하지만 로드 타임 위빙 등을 사용해야 하는데, 설정이 복잡하고 JVM 옵션을 주어야 하는 부담이 있다.
728x90
반응형
댓글