RuntimeHints
假如应用中有如下代码:
/**
* 作者:周瑜大都督
*/
public class ZhouyuService {
public string test(){
return "zhouyu";
}
}
@Component
public class UserService {
public String test(){
String result = "";
try {
Method test = ZhouyuService.class.getMethod("test", null);
result = (String) test.invoke(ZhouyuService.class.newInstance(), null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
return result;
}
}
在UserService中,通过反射的方式使用到了ZhouyuService的无参构造方法(ZhouyuService.class.newInstance()),如果我们不做任何处理,那么打成二进制可执行文件后是运行不了的,可执行文件中是没有ZhouyuService的无参构造方法的,会报如下错误:
我们可以通过Spring提供的Runtime Hints机制来间接的配置reflect-config.json。
方式一:RuntimeHintsRegistrar
提供一个RuntimeHintsRegistrar接口的实现类,并导入到Spring容器中就可以了:
@Component
@ImportRuntimeHints(UserService.ZhouyuServiceRuntimeHints.class)
public class UserService {
public String test(){
String result = "";
try {
Method test = ZhouyuService.class.getMethod("test", null);
result = (String) test.invoke(ZhouyuService.class.newInstance(), null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
return result;
}
static class ZhouyuServiceRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
try {
hints.reflection().registerConstructor(ZhouyuService.class.getConstructor(), ExecutableMode.INVOKE);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
}
方式二:@RegisterReflectionForBinding
@RegisterReflectionForBinding(ZhouyuService.class)
public String test(){
String result = "";
try {
Method test = ZhouyuService.class.getMethod("test", null);
result = (String) test.invoke(ZhouyuService.class.newInstance(), null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
return result;
}
注意
如果代码中的methodName是通过参数获取的,那么GraalVM在编译时就不能知道到底会使用到哪个方法,那么test方法也要利用RuntimeHints来进行配置。
@Component
@ImportRuntimeHints(UserService.ZhouyuServiceRuntimeHints.class)
public class UserService {
public String test(){
String methodName = System.getProperty("methodName");
String result = "";
try {
Method test = ZhouyuService.class.getMethod(methodName, null);
result = (String) test.invoke(ZhouyuService.class.newInstance(), null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
return result;
}
static class ZhouyuServiceRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
try {
hints.reflection().registerConstructor(ZhouyuService.class.getConstructor(), ExecutableMode.INVOKE);
hints.reflection().registerMethod(ZhouyuService.class.getMethod("test"), ExecutableMode.INVOKE);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
}
或者使用了JDK动态代理:
public String test() throws ClassNotFoundException {
String className = System.getProperty("className");
Class<?> aClass = Class.forName(className);
Object o = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{aClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.getName();
}
});
return o.toString();
}
那么也可以利用RuntimeHints来进行配置要代理的接口:
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.proxies().registerJdkProxy(Userinterface.class);
}
方式三:@Reflective
对于反射用到的地方,我们可以直接加一个@Reflective,前提是ZhouyuService得是一个bean:
@Component
public class ZhouyuService {
@Reflective
public ZhouyuService() {
}
@Reflective
public String test(){
return "zhouyu";
}
}
以上Spring6提供的RuntimeHints机制,我们可以使用该机制更方便的告诉GraalVM我们额外用到了哪些类、接口、方法等信息,最终Spring会生成对应的reflect-config.json、proxy-config.json中的内容,GraalVM就知道了。
![](/images/780-200-2.jpg)