해당 포스팅은 인프런에서 제공하는 김영한 님의 '스프링 핵심원리 고급편'을 수강한 후 정리한 글입니다. 유료 강의를 정리한 내용이기에 제공되는 예제나 몇몇 내용들은 제외하였고, 정리한 내용을 바탕으로 글 작성자인 저의 언어로 다시 작성한 글이기에 서술이 부족하거나 잘못된 내용이 있을 수 있습니다. 그렇기에 해당 글은 개념에 대한 참고 정도만 해주시고, 강의를 통해 학습하시기를 추천합니다.
동적 프록시를 사용할 때 인터페이스가 있는 경우에는 JDK 동적 프록시를, 구체 클래스는 CGLIB를 적용해야 한다. 이를 자동화할 수 있는 방법이 필요하며, 이 때 JDK 동적 프록시가 제공하는 InvocationHandler
와 CGLIB가 제공하는 MethodInterceptor
를 각각 중복으로 개발하여 관리해야 하는지, 특정 조건에 부합할 때 프록시 로직을 적용하는 공통 기능 제공 등 고민해야 할 부분이 존재한다.
스프링은 유사한 구체적인 기술들이 존재할 때 이를 통합해서 일관성 있게 접근하고 편리하게 사용할 수 있도록 추상화된 기술을 제공하며, 동적 프록시를 통합해서 관리하기 위해 프록시 팩토리(ProxyFactory
)를 제공한다.
프록시 팩토리를 사용할 경우 인터페이스는 JDK 동적 프록시를, 구체 클래스만 존재할 경우 CGLIB를 사용한다. 또한 이 설정을 변경할 수도 있게 해준다.
또한 두 기술을 함께 사용할 때 부가 기능을 적용하기 위해 각 기술을 중복해서 개발하지 않도록 Advice
라는 새로운 개념을 도입했다.
개발자는 InvocationHandler
와 MethodInterceptor
를 구분하지 않고 Advice
하나만을 만들면 프록시 팩토리가 기술에 맞게 해당 Advice
를 호출하는 전용 InvocationHandler
와 MethodInterceptor
를 내부적으로 사용한다.
Advice
를 만드는 방법은 기본적으로 스프링 AOP 모듈에서 제공하는 org.aopalliance.intercept
패키지의 MethodInterceptor
를 구현하면 된다.
public class TestAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("TestProxy Run");
Object result = invocation.procced();
/*
* target 클래스 호출 및 결과 반환
* target 클래스의 정보는 MethodInvocation 안에 모두 포함,
* ProxyFactory를 생성하는 단계에 이미 target 정보를 파라미터로 전달
*/
System.out.println("TestProxy Stop");
return result;
}
}
public class ProxyFactoryTest {
public void interfaceProxy() { // JDK 동적 프록시 적용
TestInterface target = new TestInterface();
ProxyFactory proxyFactory = new ProxyFactory(target);
// target 정보를 파라미터로 전달받는다.
proxyFactory.addAdvice(new TestAdvice());
TestInterface proxy = (TestInterface) proxyFactory.getProxy();
proxy.execute();
}
public void concreteProxy() { // CGLIB 적용
TestClass target = new TestClass();
ProxyFactory proxyFactory = new ProxyFactory(target);
// target 정보를 파라미터로 전달받는다.
proxyFactory.addAdvice(new TestAdvice());
TestClass proxy = (TestClass) proxyFactory.getProxy();
proxy.execute();
}
public void interfaceProxyTargetClass() {
// Interface가 존재하지만 CGLIB 적용
TestInterface target = new TestInterfaceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
// target 정보를 파라미터로 전달받는다.
proxyFactory.setProxyTargetClass(true); // 구현체에 적용
proxyFactory.addAdvice(new TestAdvice());
TestInterface proxy = (TestInterface) proxyFactory.getProxy();
proxy.execute();
}
}
스프링은 Pointcut
이라는 개념을 도입해 특정 조건에 부합할 경우에만 프록시 부가 기능이 적용되도록 설정해줄 수 있게 해준다.
프록시 팩토리는 역할과 책임을 명확하게 분리하기 위해 다음과 같은 단어들을 사용한다.