Dynamic Proxy
Dynamic proxies require special handling for GraalVM native image.
Automatic Detection
Native Image does a static analysis that detects calls the following methods:
java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)
java.lang.reflect.Proxy.getProxyClass(ClassLoader, Class<?>[])
From the method calls, Native Image tries to determine the list of interfaces that define dynamic proxies. Proxy classes are generated at image build time.
The static analysis can only support list of interfaces that comes from a constant array or an array that is allocated in the same method.
In the code below, LoggingInvocationHandler
is the InvocationHandler of the proxy instance. In the wrap
method, the incoming Callable
object is wrapped by the invocation handler and a proxy instance is returned.
package io.vividcode.graalvm.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.concurrent.Callable;
public class DynamicProxyExample {
public static void main(String[] args) throws Exception {
DynamicProxyExample proxy = new DynamicProxyExample();
Callable<?> callable = proxy.wrap(() -> "Hello World");
System.out.println(callable.call());
}
public Callable<?> wrap(Callable<?> callable) {
ClassLoader classLoader = DynamicProxyExample.class.getClassLoader();
InvocationHandler handler = new LoggingInvocationHandler<Callable<?>>(callable);
return (Callable<?>)
Proxy.newProxyInstance(classLoader, new Class<?>[] {Callable.class}, handler);
}
/** Logging method invocations */
private static class LoggingInvocationHandler<T> implements InvocationHandler {
private final T wrapped;
LoggingInvocationHandler(T wrapped) {
this.wrapped = wrapped;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(
"Before invocation of " + method.getName() + ", parameters: " + Arrays.toString(args));
Object result = method.invoke(wrapped, args);
System.out.println("After invocation of " + method.getName() + ", result: " + result);
return result;
}
}
}
When executing the native image, it correctly shows following output:
Before invocation of call, parameters: null
After invocation of call, result: Hello World
Hello World
Manual Configuration
If the list of interfaces that implemented by dynamic proxies is not constant, this list needs to be declared explicitly and passed to native-image
command. This is done by using the -H:DynamicProxyConfigurationFiles
option.
Below is an example of dynamic proxy config file. The interfaces
property represents a list of interfaces implemented by a dynamic proxy.
[
{
"interfaces": ["java.util.concurrent.Callable"]
}
]
Refer to the JSON schema of proxy config.