이 포스트는 Spring Framework Documentation에서 제공하는 Proxying Mechanisms문서의 예제를 활용하여 설명을 진행하고 있습니다.
Spring AOP의 가장 중요한 핵심인 AOP Proxy가 어떤 식으로 동작하는지 살펴봅니다.
Proxy 적용 전과 적용 후에 대해서 살펴보기 위해서 간단한 클래스를 하나 작성합니다.
// Source from Spring Framework Documentation
public class SimplePojo implements Pojo {
public void foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar();
}
public void bar() {
// some logic...
}
}
Proxy를 적용하지 않고 기존 객체를 생성 및 메서드를 사용하는 시나리오를 살펴보면 아래와 같습니다.
// Source from Spring Framework Documentation
public class Main {
public static void main(String[] args) {
Pojo pojo = new SimplePojo();
// this is a direct method call on the 'pojo' reference
pojo.foo();
}
}
Main method에서 객체 참조에 의한 메서드를 호출하면 아래 이미지처럼 해당 객체에 대한 메서드가 직접적으로 호출됩니다.
Proxy를 적용해 메서드의 참조가 프록시인 경우의 시나리오는 어떻게 이루어질까요?
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
여기서 우리가 확인해야할 점은 Main
클래스의 main
메서드에서 클라이언트 코드에 대해서 프록시 참조를 진행했다는 점입니다.
즉 main
에서 호출하는 pojo.foo()
의 경우 아래 그림과 같이 프록시 객체에서의 메서드 호출로 진행됩니다. → 이를 통해서 특정 메서드 호출과 관련된 Advice를 적용할 수 있게 되는 것입니다.
하지만 메서드 호출이 최종적으로 SimplePojo
에 도달한다면 this.bar()
와 같이 객체 자체에 대한 호출은 직접적으로 객체에 참조되어 호출이 됩니다. → 자체 호출이 있는 경우에 대해서는 메서드 호출에 의한 Advice를 적용할수 없는 것입니다.
자체 호출을 사용하지 않도록 코드를 리팩토링하는 방식이 가장 알맞는 방식이라고 할 수 있습니다.
Spring Framework Documentation에서는 아래 예시를 horrendous한 방식이라고 표현합니다.
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
위와 같은 방식으로 SimplePojo
클래스를 리팩토링하고 main
메서드에 factory.setExposeProxy(true)
를 추가한다면 SimplePojo
클래스는 Spring AOP에 완전히 적용될 수 있습니다.
AspectJ를 활용하는 경우 자체 호출과 관련된 문제를 신경쓰지 않아도 됩니다. 왜냐하면 AspectJ는 프록시 기반의 AOP 프레임워크가 아니기 때문입니다.