네이티브 이미지의 동적 프록시

2023. 4. 10. 19:44프로그래밍

728x90
 

Dynamic Proxy in Native Image

 

www.graalvm.org

 

java.lang.reflect.Proxy로 구현된 Java 동적 프록시들은 java.lang.reflect.InvocationHandler를 통해 모든 메서드 호출을 라우팅하여 객체 수준 액세스 제어를 가능하게 하는 메커니즘을 제공합니다. 

동적 프록시 클래스들은 인터페이스 목록에서 생성됩니다.

 

네이티브 이미지는 런타임에 바이트코드를 생성하고 해석하기 위한 머신을 제공하지 않습니다. 

그래서 모든 동적 프록시 클래스는 이미지 빌드 시에 생성되어야 합니다.

 

자동 감지

네이티브 이미지는 

java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)
 

 및 

InvocationHandler) and java.lang.reflect.Proxy.getProxyClass(ClassLoader, Class<?>[])
 

에 대한 호출을 감지하는 간단한 정적 분석을 사용합니다. 그런 다음 동적 프록시를 자동으로 정의하는 인터페이스 목록을 결정하려고 시도합니다. 

인터페이스 목록이 주어지면 네이티브 이미지는 이미지 빌드 시에 프록시 클래스를 생성하고 이를 네이티브 이미지 힙에 추가합니다. 

동적 프록시 클래스를 생성하는 것 외에도 java.lang.reflect.InvocationHandler 인수, 즉 

java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?> [], InvocationHandler)
 
 

는 런타임에 동적 프록시 인스턴스를 할당할 수 있도록 리플렉션에 등록됩니다.

 

분석은 인터페이스 목록이 상수 배열 또는 동일한 메서드에 할당된 배열에서 오는 상황으로 제한됩니다. 예를 들어, 아래의 코드 스니펫에서 동적 프록시 인터페이스를 자동으로 결정할 수 있습니다.

 

Static Final 어레이:

class ProxyFactory {

    private static final Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};

    static Comparator createProxyInstanceFromConstantArray() {
        ClassLoader classLoader = ProxyFactory.class.getClassLoader();
        InvocationHandler handler = new ProxyInvocationHandler();
        return (Comparator) Proxy.newProxyInstance(classLoader, interfaces, handler);
    }
}
 

분석은 소스 코드가 아닌 컴파일러 그래프에서 작동하는 것에 유의 하십시오.

따라서 배열을 선언하고 채우는 다음 방법은 분석 관점에서 동일합니다:

private static final Class<?>[] interfacesArrayPreInitialized = new Class<?>[]{java.util.Comparator.class};
 
private static final Class<?>[] interfacesArrayLiteral = {java.util.Comparator.class};
 
private static final Class<?>[] interfacesArrayPostInitialized = new Class<?>[1];
static {
    interfacesArrayPostInitialized[0] = java.util.Comparator.class;
}
 

그러나 Java에는 불변 배열이 없습니다. 배열이 static final로 선언되더라도 나중에 내용이 변경될 수 있습니다.

여기에 사용된 간단한 분석은 어레이에 대한 추가 변경 사항을 추적하지 않습니다.

 

새로운 어레이:

class ProxyFactory {

    static Comparator createProxyInstanceFromNewArray() {
        ClassLoader classLoader = ProxyFactory.class.getClassLoader();
        InvocationHandler handler = new ProxyInvocationHandler();
        Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};
        return (Comparator) Proxy.newProxyInstance(classLoader, interfaces, handler);
    }
}
 

참고: 상수 배열과 마찬가지로 배열을 선언하고 채우는 다음 방법은 분석 관점에서 동일합니다.

Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};
 
Class<?>[] interfaces = new Class<?>[1];
interfaces[0] = Question.class;
 
Class<?>[] interfaces = {java.util.Comparator.class};
 

정적 분석은 동적 프록시 클래스를 정의하는 데 가장 자주 사용되는 코드 패턴에 포함됩니다.

분석에서 인터페이스 배열을 검색할 수 없는 예외적인 경우에는 수동 동적 프록시 구성 메커니즘도 있습니다.

 

관련 문서

  • Configure Dynamic Proxies Manually
  • Reachability Metadata: Dynamic Proxy

 

이상.

 

728x90