동적 프록시 - (2) JDK 동적 프록시

Drumj·2023년 2월 8일
0

오늘의 학습

프록시를 사용하는 프록시 패턴, 데코레이터 패턴을 배운 이후

프록시 클래스들의 소스 코드는 거의 동일한 모양을 하고 있는데 이 프록시를 적용하기 위해서
계속 프록시 클래스들을 만들어야 하는 문제점이 있었다.

동적 프록시 기술을 이용해서 쉽게 적용시켜보자!

  1. 리플렉션
  2. JDK 동적 프록시
  3. CGLIB

2. JDK 동적 프록시

본격적으로 동적 프록시를 공부해 보자.

JDK 동적 프록시는 인터페이스를 기반으로 프록시를 동적으로 만들어주기 때문에 인터페이스가 필수이다!

간단한 예제코드

public interface AInterface {
	String call();
}

@Slf4j
public class AImpl implements AInterface {
	@Override
	public String call() {
		log.info("A 호출");
		return "a";
	}
}

BInterface와 BImpl은 생략한다.

JDK 동적 프록시에 사용할 로직은 InvocationHandler 인터페이스를 구현해서 작성한다.

그러면 InvocationHandler을 구현한 TimeInvocationHandler를 만들어보자.

@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;
    }
}

그리고 Test Code를 작성해서 사용해보자

//(생략)
@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());
 }
 //(생략)

proxy.call(); 을 통해서 TimeInvocationHandler -> AImpl 실행된다.
결국 나머지 코드들은 설정해주는 코드에 불과하다.
실제로 애플리케이션 적용에서도 Config 파일을 보면 이렇게 복잡하게 되어있으나 실제 동작되는 원본 코드들은 여전히 깔끔하게 남아있는 걸 알 수 있다.

애플리케이션에 적용하는 코드는 생략하도록 하겠다.


P.S 사실 정리하는 지금도 잘 정리를 한 건지, 제대로 이해를 한 건지 모르겠다. 좀 어렵다.

0개의 댓글