저번 시간에 Proxy를 만들 때 JDK 동적 프록시를 사용할 때는 InvocationHandler를, CGLIB를 사용할때는 MethodInterceptor를 각각 구현했어야했다.
매번 이렇게 다른 것을 해결해 주는 것이 ProxyFactory이다.
팩토리 하나로 편리하게 동적 프록시를 생성할 수 있으며 프록시 패곹리는 인터펭이스가 있다면 JDK 동적 프록시를 사용하고, 구체 클래스만 있다면 CGLIB를 사용한다.
ProxyFactory 흐름
스프링은 위와같은 인터페이스, 구체클래스일 때 부가 기능을 적용할 때 Advice라는 새로운 개념을 도입했다.
개발자는 InvocationHandler나 MethodInterceptor는 Advice만 만들면 된다.
프록시 팩토리를 사용하면 Advice를 호출하는 InvocationHandler, MethodInterceptor를 내부에서 사용한다.
Advice 도입 후 흐름
결국, 클라이언트 호출시 우리는 실제 타겟이 아닌 프록시를 호출하게 되고, 프록시 객체들은 JDK 동적 프록시라면 내부적으로 adviceInvocationHandler를 호출하는데 이 핸들러는 handler.invoke()를 사용하여 프록시의 동작을 정의한다.
CGLIB 프록시라면 내부적으로 adviceMethodInterceptor를 호출하는데 이 인터셉터는 intercept()를 사용하여 프록시의 동작을 정의한다.
결국 부가기능을 Advice에서 담당하게 된다. Advice는 기존 객체, 타겟으로부터 부가기능만을 추가하는 기능을 제공한다.
Advice 생성
@Slf4j
public class TimeAdvice implements MethodInterceptor {
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
log.info("TimeProxy 실행");
long startTime = System.currentTimeMillis();
// Object result = method.invoke(target, args);
// target을 자동으로 찾고, args와 함께 반환되는 것으로 반환
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy 종료 resultTime={}", resultTime);
return result;
}
}
ProxyFactory 도입 및 테스트
@Slf4j
public class ProxyFactoryTest {
@Test
@DisplayName("인터페이스가 있으면 JDK 동적 프록시 사용")
void interfaceProxy() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvice(new TimeAdvice());
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.save();
// AopUtils 는 ProxyFactory를 통해서 만들었어야만 가능함
boolean isProxy = AopUtils.isAopProxy(proxy);
Assertions.assertThat(isProxy).isTrue();
boolean isJdkProxy = AopUtils.isJdkDynamicProxy(proxy);
Assertions.assertThat(isJdkProxy).isTrue();
boolean isCglibProxy = AopUtils.isCglibProxy(proxy);
Assertions.assertThat(isCglibProxy).isFalse();
}
}
이와 같이 사용해볼 수 있다.
ProxyFactory를 사용하여 쉽게 프록시를 새로 생성한다. 이 때 Advice와 기존 타겟 객체를 생성하여 쉽게 프록시 객체를 만들 수 있다.
'Spring > Spring AOP' 카테고리의 다른 글
Spring AOP 6. 포인트컷, 어드바이스, 어드바이저 (0) | 2024.03.20 |
---|---|
Spring AOP 4. 동적 프록시: JDK 동적프록시와 CGLIB (0) | 2024.03.08 |
Spring AOP 3. 디자인 패턴: 데코레이터 패턴 (0) | 2024.03.06 |
Spring AOP 2. 디자인 패턴: 프록시패턴 (0) | 2024.03.06 |
Spring AOP 1. 디자인 패턴: 전략 패턴 - 템플릿 메서드 패턴 (0) | 2024.03.01 |