개발일기장

Spring Proxy Factory 본문

Spring Boot

Spring Proxy Factory

게슬 2023. 12. 5. 13:06
728x90

Java에서 Proxy사용하는 방법에 대해서 두가지 했었음

1. Jdk 동적 Proxy

>> Interface가 있어야함. InvocationHandler를 구현해서 객체 생성 후 Proxy를 생성해서 사용

https://tlqckd0.tistory.com/102

 

Proxy ...

Proxy 사용법. 1. InvocationHandler를 구현한 객체를 만듬 생성자에 실제 method를 수행할 객체를 넣어줘야함.. 그리고 다른것도 넣어도 될듯..? args에는 method를 호출 했을 때 넣어주는 인자값들. @Slf4j publ

tlqckd0.tistory.com

 

2. CGLIB

>> 인터페이스없이 구체 클레스(인터페이스도 됨)를 사용. MethodInterceptor를 구현 후 Enhancer에 등록 후 Proxy 생성

https://tlqckd0.tistory.com/103

 

CGLIB proxy ...

일반 java proxy 말고 CGLIB에서 제공하는 proxy사용법 1. MethodInterceptor를 구현 methodProxy 객체가 있는것 말고는 여기까지는 비슷함 그리고 method.invoke보다 저게 더 빠르다고 하는데 몰루겠슴. @Slf4j public

tlqckd0.tistory.com

이렇게 두가지 했었음.


1. Spring이 제공하는 ProxyFactory

spring은 위에 두가지(interface인 경우 JDK동적프록시, 구체클레스인 경우 CGLIB) 기능을 편하게 사용할 수 있는 

ProxyFactory를 제공한다.

 

이때에도 MethodInterceptor를 구현해야 하는데 이거는 CGLIB이랑 다른 package임

import org.aopalliance.intercept.MethodInterceptor;  // << 이거 해야함
import org.springframework.cglib.proxy.MethodInterceptor; // << 이거 아님;;

 

일단 MethodInterceptor를 구현해서 Advice를 만들면 되는데 이 때 invoke <ㅡ 이놈은 invocation.proceed()로 수행함.

그리고 이거는 실제 호출 대상을 ProxyFactory생성 시점에 넣어주기 때문에 그거를 위한 작업은 따로 안해도 됨

@Slf4j
public class TimeAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        log.info("Proxy 실행");
        Object result = invocation.proceed(); // invoke -> target을 찾아서 호출해줌.
        log.info("TimeProxy 종료 ");
        return result;
    }
}

 

이렇게 한 다음 실행 시키려면 ProxyFactory 생성 + Advice 등록만 하면 된다.

    void interfaceProxy(){
        ServiceInterface target = new ServiceImpl();
        // 1. ProxyFactory 생성(호출 클래스 넣어줌)
        ProxyFactory proxyFactory = new ProxyFactory(target);
        //proxyFactory.setProxyTargetClass(true); // -> 항상 CGLIB 기반
        
        // 2. Advice 등록
        proxyFactory.addAdvice(new TimeAdvice());
        ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

        log.info("target class = {}", target.getClass());
        log.info("proxy class = {}", proxy.getClass());
        proxy.save();

        Assertions.assertThat(AopUtils.isAopProxy(proxy)).isTrue();
        Assertions.assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue();
        Assertions.assertThat(AopUtils.isCglibProxy(proxy)).isFalse();
    }

이렇게 하고 실행시키면 아래와 같은 결과가 나온다.

$Proxy10 <- JDK 동적 프록시

 


2. Advice, Pointcut, Advisor

Advice(MethodInterceptor구현)는 로직, Pointcut은 필터이고 이거를 합치면 Advisor임..(비문학 못함)

이거를 직접 생성하는 경우도 있겠지만 그럴리 없다고함.. Spring에서 다 제공해주기 때문에

Advisor의 경우 DefaultPointcutAdvisor를 사용하면 됨

    void advisorTest3(){
        ServiceInterface target = new ServiceImpl();
        ProxyFactory proxyFactory = new ProxyFactory(target);

        // 1. Pointcut 생성
        NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
        // method 이름이 save 인 경우에만 Advice(proxy)적용
        nameMatchMethodPointcut.setMappedName("save");

        // 2. Advisor 생성 (Pointcut, advice) 주입
        DefaultPointcutAdvisor advisor 
        	= new DefaultPointcutAdvisor(nameMatchMethodPointcut, new TimeAdvice());

        // 3. ProxyFactory 에 Advisor 등록
        proxyFactory.addAdvisor(advisor);
        ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

        proxy.save();
        proxy.find(); // 이건 proxy 적용 X

    }

이렇게 작성을 하면 method이름이 save인 것만 advise가 적용되고 아래와 같은 경과가 나옴

save Pointcut 적용


3. Advisor 여러개 적용

    void multiProxy2(){
        ServiceInterface target = new ServiceImpl();
        ProxyFactory proxyFactory = new ProxyFactory(target);
        DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
        DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
        //넣은 순서대로 호출해줌 ..
        proxyFactory.addAdvisor(advisor2);
        proxyFactory.addAdvisor(advisor1);
        ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

        proxy.save();
    }

그냥 이렇게 등록해주면 되는데 이러는 경우 등록한 순서대로 호출이 됨.

Advisor 여러개 등록


Advisor를 Bean으로 등록해두면 자동으로 사용이 됨..

    @Bean
    public Advisor advisor(Object obj) {
        //pointcut
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(" aop .. ");
        
        //advice
        AdviceLogic advice = new AdviceLogic(obj);
        return new DefaultPointcutAdvisor(pointcut, advice);
    }
728x90
Comments