동적 프록시

박윤택·2024년 1월 1일
0

JAVA

목록 보기
14/14

1. 리플렉션

클래스/메서드 메타정보를 이용하여 실제 인스턴스를 호출하는 방법

public class ReflectionTest {

	public String methodA() {
    	return "methodA";
    }
}


Class reflectionClass = Class.forName("com.demo.test.ReflectionTest");
ReflectionClass target = new ReflectionClass();

Method methodA = reflectionClass.getMethod("methodA");
Object result = methodA.invoke(target);

주의

리플렉션을 사용하면 클래스와 메서드의 메타정보를 사용해서 어플리케이션에서 동적으로 사용할 수 있지만 런타임에 동작하기 때문에 컴파일 시점에서 오류를 찾기 힘들다.


2. JDK 동적 프록시

InvocationHandler

JDK 동적 프록시는 인터페이스를 기반으로 프록시를 동적으로 생성한다.

package java.lang.reflect;

public interface InvocationHandler {

	public Object invoke(Object proxy, Method method, Object[] args) thrw Trhwable;
}
  • Object proxy : 프록시
  • Method method : 호출한 메서드
  • Object[] args : 메서드를 호출할 때 전달한 인수

예시

public interface AInterface {
	String call();
}



public class AClass implements AInterface {
	@Override
    public String call() {
    	return "call";
    }
}



public class CustomInvocationHandler implements InvocationHandler {
	private final Object target;
    
    public CustomInvocationHandler(Object object) {
    	this.target = object;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) thrw Trhwable {
    	Object result = method.invokde(target, args);
        return result;
    }

}



@Slf4j
class JDKDynamicProxyTest {
	@Test
    void test() {
    	AInterface target = new AClass();
        CustomInvocationHandler handler = new CustomInvocationHandler(target);
        AInterface proxy = (AInterface) Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[]{AInterface.class}, handler);
        String result = (String) proxy.call(); // "call" 
        log.info("targetClass={}", target.getClass()); // class com.demo.test.AClass
        log.info("proxyClass={}", proxy.getClass()); // proxyClass=class com.sun.proxy.$Proxy1
    }

}

3. CGLIB

바이트 코드를 조작해서 동적으로 클래스를 생성하는 기술을 제공하는 라이브러리, 인터페이스가 없어도 구체 클래스만 가지고 동적 프록시 생성 가능

MethodInterceptor

package org.springframework.cglib.proxy;

  public interface MethodInterceptor extends Callback {
     Object intercept(Object obj, Method method, Object[] args, MethodProxy
 proxy) throws Throwable;
 }
  • obj : CGLIB가 적용된 객체
  • method : 호출된 메서드
  • args : 메서드를 호출하면서 전달되는 인수
  • proxy : 메서드 호출에 사용

예시

public class AClass {
    public String call() {
    	return "call";
    }
}


public class CustomMethodInterceptor implements MethodInterceptor {
	private final Object target;
    
    public CustomMethodInterceptor(Object object) {
    	this.target = object;
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) thrw Trhwable {
    	Object result = method.invokde(target, args);
        return result;
    }

}


@Slf4j
class CGLIBTest {
	@Test
    void test() {
    	AClasss target = new AClass();
        
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(AClass.class); // 구체 클래스 상속
        enhancer.setCallback(new CustomMethodInterceptor(target));
        AClass proxy = (AClass) enhancer.create();
        
        log.info("targetClass={}", target.getClass()); // com.demo.test2.AClass
        log.info("proxyClass={}", proxy.getClass()); // com.demo.test2.AClass$$EnhancerByCGLIB$$25d6b0e3
        proxy.call();
    }

}

제약 조건

  • 부모 클래스의 생성자 체크
    * CGLIB는 자식 클래스 동적으로 생성하기 때문에 기본 생성자 필요
  • final 키워드 붙은 클래스 상속 불가능
  • final 메서드 오버라이딩 불가

0개의 댓글