[Spring] ProxyFactory
μ΄μ ν¬μ€ν μμ λμ νλ‘μλ₯Ό μ μ©ν λμ λ¬Έμ μ μ μ§μ΄λ΄€λ€.
- μΈν°νμ΄μ€κ° μλ κ²½μ°μλ CGLIBλ₯Ό, μλ κ²½μ°μλ JDK λμ νλ‘μλ₯Ό μ μ©ν΄μΌνλ€.
- λ κΈ°μ μ λͺ¨λ μ¬μ©νλ €λ©΄, JDK λμ νλ‘μμ InvocationHandler μ CGLIBμ MethodInterceptorλ₯Ό μ€λ³΅ν΄μ λ§λ€μ΄ κ΄λ¦¬ν΄μΌν κΉ?
- νΉμ 쑰건μμ(νν°) νλ‘μ λ‘μ§μ μ μ©νλ κΈ°λ₯μ 곡ν΅μΌλ‘ μ μ©ν μ μμ΄μΌ νλ€.
μ€νλ§μμ μ 곡νλ μΆμνλ νλ‘μ κΈ°μ : ProxyFactory
μ€νλ§μ λμ νλ‘μλ₯Ό ν΅ν©νμ¬ μμ±ν΄ μ£Όλ νλ‘μ ν©ν 리(Proxy Factory) λΌλ κΈ°λ₯μ μ 곡νλ€. μ΄μ μλ μΈν°νμ΄μ€μ μ 무μ λ°λΌ JDK λμ νλ‘μλ CGLIB μ μ νν΄μΌ νλ€λ©΄, μ΄μ λ Proxy Factory νλλ‘ νΈλ¦¬νκ² λμ νλ‘μλ₯Ό μμ±ν μ μλ€. Proxy Factory μ μμ‘΄ κ΄κ³μ μ¬μ© νλ¦μ μλ κ·Έλ¦Όκ³Ό κ°λ€.
Advice , Pointcut λμ
μ€νλ§μ Advice λΌλ μλ‘μ΄ κ°λ μ λμ νλ©΄μ, μν©μ λ°λΌ JDK λμ νλ‘μλ CGLIB λ₯Ό λ°λ‘ μμ±ν΄μΌνλ λ¬Έμ λ₯Ό ν΄κ²°νλ€. κ°λ°μλ InvocationHandler λ MethodInterceptor λ₯Ό μ κ²½μ°μ§ μκ³ , Advice λ§ μμ±νλ©΄ λλ€. ( Advice λ κ°λ°μκ° μ μ©νκ³ μΆμ λΆκ° κΈ°λ₯ λ‘μ§μ΄λΌ μκ°νμ )
νλ‘μ ν©ν 리λ₯Ό μ¬μ©νλ©΄, λ΄λΆμμ Advice λ₯Ό νΈμΆνλ μ μ© InvocationHandler, MethodInterceptor λ₯Ό λ΄λΆμμ μ¬μ©νλ€.
μ΄μ μ FilterHandler λ₯Ό λ§λ€μ΄, νΉμ 쑰건μ λ§λ λ©μλμλ§ νλ‘μλ₯Ό μ μ©ν μ μ΄ μλ€. νλ‘μ ν©ν 리μμλ Pointcut μ΄λΌλ κ°λ μ λμ νμ¬ μ΄ λ¬Έμ λ₯Ό μΌκ΄μ± μκ² ν΄κ²°νλ€. μμ μ½λλ₯Ό 보면μ Advice μ Pointcut μ λ μ΄ν΄ν΄λ³΄μ.
Advice, Pointcut μμ μ½λ
Advice λ λΆκ°κΈ°λ₯ λ‘μ§μ λ΄λΉνλ€κ³ νλ€. μ¬κΈ°μ TimeAdvice λ λ©μλμ μ€ν μκ°μ 체ν¬νμ¬ λ‘κ·Έλ₯Ό λ¨κΈ°λ λΆκ°κΈ°λ₯μ λ΄λΉνλ€. Advice λ MethodInterceptor
λ₯Ό μμλ°μ invoke(MethodInvocation invocation)
λ©μλλ₯Ό μ€λ²λΌμ΄λ νλλ°, μ΄ λ invocation.proceed()
λ₯Ό νΈμΆνλ©΄ targe ν΄λμ€λ₯Ό νΈμΆνκ³ κ·Έ κ²°κ³Όλ₯Ό 리ν΄λ°λλ€. κ·Έλ°λ° μ΄μ ν¬μ€ν
κ³Ό λ¬λ¦¬ target μ μ λ³΄κ° λ³΄μ΄μ§ μλλ€. μ΄μ μλ μμ±μλ‘ target μ μ£Όμ
λ°μμ§λ§, MethodInterceptor λ₯Ό μ¬μ©νλ©΄, MethodInvocation invocation λ΄λΆμ target μ ν΄λμ€ μ λ³΄κ° λͺ¨λ ν¬ν¨λΌ μλ€.
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@Slf4j
public class TimeAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("TimeProxy μ€ν");
long startTime = System.currentTimeMillis();
// κΈ°μ‘΄
//Object result = method.invoke(target, args);
// λ³κ²½ : MethodInterceptor μμ ν
Object result = invocation.proceed();
// target μ μλμΌλ‘ μ°Ύκ³ μΈμλ₯Ό λκΈ΄λ€. -> μ€μ νκ²μ νΈμΆνλ€.
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy μ’
λ£ resultTime={}", resultTime);
return result;
}
}
μ£Όμν μ μ, TimeAdvice κ° μμλ°λ MethodInterceptor
κ° CGLIBμ MethodInterceptor μ μ΄λ¦μ΄ κ°μΌλ―λ‘ ν¨ν€μ§ μ΄λ¦μ μ£Όμν΄μΌνλ€. Advice λ₯Ό μ μ©νκΈ° μν΄μλ org.aopalliance.intercept
μ€νλ§ AOP λͺ¨λ λ΄λΆμ μλ κ²μ μ¬μ©νλ€.
μ΄μ μ°λ¦¬κ° λ§λ Advice λ₯Ό ProxyFactory μ μ λ¬νλ©΄ proxy κ° λ°νλκ³ μ μ©λλ κ²μ νμΈν μ μλ€.
Advice - ProxyFactory μ μ© (1) : μΈν°νμ΄μ€λ₯Ό μμλ°λ target μ proxy μμ±
- μ°μ Proxy λ₯Ό μ μ©ν λμ (target) κ°μ²΄λ₯Ό λΆλ¬μμΌνλ€. μ¬κΈ°μλ ServiceInterface νμ μ ServiceImpl μ μΈμ€ν΄μ€λ₯Ό target μΌλ‘ μ§μ νλ€. ν΄λΉ μΈμ€ν΄μ€λ μΈν°νμ΄μ€λ₯Ό μμλ°κ³ μμμ μΈμ§νμ
- κ·Έ λ€μ ProxyFactory() μΈμ€ν΄μ€λ₯Ό μμ±ν λ μΈμλ‘ target κ°μ²΄λ₯Ό λ겨μ€λ€. νλ‘μ ν©ν 리λ μ΄ μΈμ€ν΄μ€ μ 보λ₯Ό κΈ°λ°μΌλ‘ λ΄λΆμμ νλ‘μλ₯Ό μμ±νλ€.
- μ°λ¦¬κ° λκ²¨μ€ μΈμ€ν΄μ€λ μΈν°νμ΄μ€λ₯Ό μμλ°μλ€κ³ μΈκΈνλ€. μ¦ target μΈμ€ν΄μ€μ μΈν°νμ΄μ€κ° μλ€λ©΄ ProxyFactory λ΄λΆμμ JDK λμ νλ‘μλ₯Ό κΈ°λ³ΈμΌλ‘ μ¬μ©νκ³ , κ·Έλ μ§ μλ€λ©΄ CGLIB λ₯Ό ν΅ν΄ λμ νλ‘μλ₯Ό μμ±νλ€.
proxyFactory.addAdvice()
: μ΄μ νλ‘μκ° μ¬μ©ν λΆκ°κΈ°λ₯ λ‘μ§μ addAdvice() μ μΈμλ‘ λ겨μ€λ€. μ°λ¦¬κ° λ§λ TimeAdvice μ μΈμ€ν΄μ€λ₯Ό λ겨주μ. ( JDK λμ νλ‘μκ° μ 곡νλ InvocationHandler μ CGLIBκ° μ 곡νλ MethodInterceptorμ κ°λ κ³Ό μ μ¬)- λΆκ°κΈ°λ₯ κΉμ§ μ€μ μ λ§μΉ proxyFactory λ
proxyFactory.getProxy()
λ‘ νλ‘μ κ°μ²΄λ₯Ό μμ±νκ³ κ²°κ³Όλ₯Ό λ°λλ€.
@Slf4j
public class ProxyFactoryTest {
@Test
@DisplayName("μΈν°νμ΄μ€κ° μμΌλ©΄ JDK λμ νλ‘μ μ¬μ©")
void interfaceProxy() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
// target μ λ£μΌλ©΄μ νλ‘μ ν©ν 리λ₯Ό λ§λ λ€. μ¦ νλ‘μ ν©ν 리λ μ΄λ―Έ νκ²μ μ 보λ₯Ό μκ³ μλ€.
// νλ‘μμ νΈμΆλμ(target)μ νλ‘μ ν©ν 리λ₯Ό μμ±ν λ μΈμλ‘ λκΈ°λ κ².
proxyFactory.addAdvice(new TimeAdvice()); // advice μΆκ° -> νλ‘μκ° μ¬μ©ν λΆκ° λ‘μ§μ μ€μ νλ€. (advice : μ‘°μΈμ ν΄μ€λ€.)
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.save(); // νλ‘μ κ°μ²΄μ μ체 λ©μλλ₯Ό νΈμΆνλ©΄, advice λ‘ μΆκ°λ (λ‘κ·Έ) λ‘μ§μ΄ μνλλ€.
// save() μ μ μλ "save νΈμΆ" μ΄λ λ‘κ·Έκ° λ°λ‘ μ°νλ κ²μ΄ μλλΌ, TimeAdvice() λ΄λΆμμ invocation.proceed() ν λ save()μ λ‘κ·Έκ° μ°νλ€,.
assertThat(AopUtils.isAopProxy(proxy)).isTrue();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue(); // μΈν°νμ΄μ€κ° μ‘΄μ¬νλ―λ‘ JDK dynamic proxy
assertThat(AopUtils.isCglibProxy(proxy)).isFalse();
}
}
μ€ν κ²°κ³Ό
Advice - ProxyFactory μ μ© (2) : ꡬ체 ν΄λμ€λ₯Ό μ¬μ©νλ νλ‘μ μμ±
(1) μμ μ½λμ ν¬κ² λ¬λΌμ§ κ²μ μλ€. μ°¨μ΄μ μ΄λΌλ©΄ μΈν°νμ΄μ€κ° μλ ꡬ체ν΄λμ€λ₯Ό μ¬μ©νλ€λ μ μ΄λ€. λκ°μ΄ νλ‘μ ν©ν 리λ₯Ό μ¬μ©ν΄ νλ‘μλ₯Ό μμ±ν ν , ν μ€νΈμ½λλ₯Ό ν΅ν΄ μ΄λ€ κ²°κ³Όκ° λμ€λμ§ νμΈν΄λ³΄μ.
@Slf4j
public class ProxyFactoryTest {
@Test
@DisplayName("ꡬ체 ν΄λμ€λ§ μμΌλ©΄ CGLIB μ¬μ©")
void concreteProxy() {
ConcreteService target = new ConcreteService();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvice(new TimeAdvice());
ConcreteService proxy = (ConcreteService) proxyFactory.getProxy();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.call();
assertThat(AopUtils.isAopProxy(proxy)).isTrue();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isFalse();
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
}
}
assertThat(AopUtils.isCglibProxy(proxy)).isTrue() κ° ν΅κ³Όνλ κ²μ 보μ CGLIB λ₯Ό νμ©ν΄ νλ‘μκ° μμ±λμμ νμΈν μ μλ€.
Advice - ProxyFactory μ μ© (3) : ProxyTargetClass μ΅μ μ¬μ©
target μΈμ€ν΄μ€λ μΈν°νμ΄μ€λ₯Ό μμλ°μ§λ§, setProxyTargetClass(true)
μ΅μ
μ μ¬μ©νλ©΄ JDK λμ νλ‘μ λμ κ°μ λ‘ CGLIB λ₯Ό μ¬μ©ν΄ νλ‘μλ₯Ό μμ±ν μ μλ€.
@Slf4j
public class ProxyFactoryTest {
@Test
@DisplayName("ProxyTargetClass μ΅μ
μ μ¬μ©νλ©΄ μΈν°νμ΄μ€κ° μμ΄λ CGLIBλ₯Ό μ¬μ©νκ³ , ν΄λμ€ κΈ°λ° νλ‘μ μ¬μ©")
void proxyTargetClass() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.setProxyTargetClass(true);
// proxy λ₯Ό λ§λλλ° target ν΄λμ€λ₯Ό κΈ°λ°μΌλ‘ λ§λ€κΊΌμΌ! λΌκ³ μ€μ
proxyFactory.addAdvice(new TimeAdvice());
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.save();
assertThat(AopUtils.isAopProxy(proxy)).isTrue();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isFalse();
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
}
}
μ 리
νλ‘μ ν©ν 리μ μΆμν λλΆμ ꡬ체μ μΈ CGLIB, JDK λμ νλ‘μ κΈ°μ μ μμ‘΄νμ§ μκ³ νΈλ¦¬νκ² λμ νλ‘μλ₯Ό μμ±ν μ μκ² λλ€. λν νλ‘μμ λΆκ°κΈ°λ₯ λ‘μ§λ νΉμ κΈ°μ μ μ’ μλμ§ μκ³ , Advice νλλ‘ νΈλ¦¬νκ² μ¬μ©ν μ μμλ€. μ΄κ²μ νλ‘μ ν©ν 리 λ΄λΆμμ JDK λμ νλ‘μμΈ κ²½μ° InvocationHandler κ° Advice λ₯Ό νΈμΆνκ³ , CGLIB μΈ κ²½μ° MethodInterceptor κ° Advice λ₯Ό νΈμΆνλλ‘ κ°λ°λμκΈ° λλ¬Έμ΄λ€ .
μ½λ λ° μλ£ μΆμ²