프록시를 이용해서 원본 코드를 그대로 냅두고 로그 출력 로직을 도입할 수 있었다.

하지만 만약 적용해야할 곳이 100곳 이라면 100개의 프록시 클래스를 우리가 일일히 정의를 해주어야 한다는 단점이 있었다.

이를 해결하는 방법으로 JDK 동적 프록시와 CGLIB가 있다.

JDK 동적 프록시

첫 번째로 JDK 동적 프록시다.

코드와 클래스 다이어그램을 보고 이해해 보자

package hello.proxy.jdkdynamic.code;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@Slf4j
public class TimeInvocationHandler implements InvocationHandler {
    private final Object target;
    public TimeInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws
            Throwable {
        log.info("TimeProxy 실행");
        long startTime = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("TimeProxy 종료 resultTime={}", resultTime);
        return result;
    }
}

우선 원래 적었던 프록시 로직을 저 InvocationHandler 인터페이스를 구현하는 클래스에 적어 놓는다. 그리고 리플렉션으로 프록시가 호출되는 Method 정보 및 args 즉 인자들이 넘겨 지는데

이것을 이용해서 target을 호출하면 된다.

(코드로 분석결과 저 Method는 인터페이스 메서드 정보임 참고)

그리고 부가정보를 다른 곳에 적어 놓으면 된다.

package hello.proxy.jdkdynamic;
import hello.proxy.jdkdynamic.code.*;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Proxy;
@Slf4j
public class JdkDynamicProxyTest {
    @Test
    void dynamicA() {
        AInterface target = new AImpl();
        TimeInvocationHandler handler = new TimeInvocationHandler(target);
        AInterface proxy = (AInterface)
                Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[]
                        {AInterface.class}, handler);
        proxy.call();
        log.info("targetClass={}", target.getClass());
        log.info("proxyClass={}", proxy.getClass());
    }
    @Test
    void dynamicB() {
        BInterface target = new BImpl();
        TimeInvocationHandler handler = new TimeInvocationHandler(target);
        BInterface proxy = (BInterface)
                Proxy.newProxyInstance(BInterface.class.getClassLoader(), new Class[]
                        {BInterface.class}, handler);
        proxy.call();
        log.info("targetClass={}", target.getClass());
        log.info("proxyClass={}", proxy.getClass());
    }
}