
해당 포스팅은 인프런에서 제공하는 김영한 님의 '스프링 핵심원리 고급편'을 수강한 후 정리한 글입니다. 유료 강의를 정리한 내용이기에 제공되는 예제나 몇몇 내용들은 제외하였고, 정리한 내용을 바탕으로 글 작성자인 저의 언어로 다시 작성한 글이기에 서술이 부족하거나 잘못된 내용이 있을 수 있습니다. 그렇기에 해당 글은 개념에 대한 참고 정도만 해주시고, 강의를 통해 학습하시기를 추천합니다.
동적 프록시를 사용할 때 인터페이스가 있는 경우에는 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이라는 개념을 도입해 특정 조건에 부합할 경우에만 프록시 부가 기능이 적용되도록 설정해줄 수 있게 해준다.
프록시 팩토리는 역할과 책임을 명확하게 분리하기 위해 다음과 같은 단어들을 사용한다.