从GraalVM到Quarkus系列
A000篇-忽悠你用GraalVM
A001篇-NativeImage相关的注解
B001篇-NativeImage相关的注解@TargetClass
A002篇-GraalVM中的动态代理
从GraalVM到Quarkus系列-A002篇-GraalVM中的动态代理
现状
在GraalVM NativeImage中目前只支持JDK的动态代理,而且不支持运行时动态代理
简单了解动态代理
这里已经有前人种树,我们摘果子…文章在->这
在NativeImage中怎么用?
Mapper接口和实体类
代码如下:
package cbs.demo.mapper;
import cbs.demo.domain.Patient;
public interface PatientMapper {
Patient getPatient(Integer id);
}
package cbs.demo.domain;
public class Patient {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
main 函数
代码如下(这里折叠了一些东西,后面会有完整代码在gitee):
mvn clean package native-image
三连(具体可以看前面章节)
运行输出如下:
- 蓝色箭头是我们实际调用的地方
- 红色箭头是代理方法执行的第一个输出
- 黄色箭头是我们输出的调用的方法名
- 等等!!!
- 这他喵的不是和普通JDK动态代理一样么?
- 这不也是运行时动态代理的么?
我们试下完全运行时的动态代理
- 代理的接口完全是由参数在运行是传入进行动态代理
- 这样避免NativeImage在静态分析时进行一些魔法操作
- 如果这样能成功那才算是运行时的动态代理
- 运行结果如下
- 可以看到运行是失败的
- 而且明确说不支持运行时动态代理,且需要在构建时进行
- 因垂死听…那这两种有什么区别?
- 难道一开始那种写法动态代理不发生在运行时?
深入剖析
- 我去搂了一眼GraalVM的代码,发现了这个骚操作
- 这两个方法是
Proxy
类中间接被Proxy.getProxyClass
和Proxy.newProxyInstance
调用的 - 关于
@TargetClass
是干什么的去看B001的内容 - 这里在NativeImage模式下
Proxy
已经不是以前我们认识的Proxy
了,newProxyInstance
的逻辑在子方法getProxyConstructor
中被修改了 - 它是从这一坨代码中取出的代理对象
final Class<?> cl = ImageSingletons.lookup(DynamicProxyRegistry.class).getProxyClass(interfaces); try { final Constructor<?> cons = cl.getConstructor(InvocationHandler.class); if (!Modifier.isPublic(cl.getModifiers())) { cons.setAccessible(true); } return cons; } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); }
- 这坨代码大多数我们都认识,尤其是
InvocationHandler
,这就是动态代理的接口啊 - 但是
ImageSingletons
和DynamicProxyRegistry
是干啥的? 因垂死听 - 这里有
getProxyClass
,那什么时候放进去的? - 是不是有个
addProxyClass
?
- 果然,接口
DynamicProxyRegistry
有个addProxyClass
,而且有两个地方调用了它,盘它!!! - 先盘
DynamicProxyFeature.class
下的,代码链接
- 这里是处理
proxy-config.json
文件的,官方文档有讲怎么用json文件配置动态代理,这里不多做解释,文档讲的很清楚 - 然后再看
SubstrateGraphBuilderPlugins.class
下的,代码链接
- 注释说的也很清楚,就是静态分析的时候扫描字符串常量,进行注册
- 什么字符串常量呢?继续找调用者
- 找到了这两个字符串常量
- 也就是说,NativeImage在编译的时候,会将代码中有这两个字符串常量的动态代理自动注册,用的也是
ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(interfaces)
- 这就解释了为什么我们明文写到代码中动态代理可以,而用参数传入进行动态代理不可以
- 因为明文写到代码中,NativeImage在编译的时候已经自动生成代理并添加到NativeImage中了,所以在实际运行时直接取出来就行,并没有进行实际的动态代理操作
- 等等!!!你以为这样就完了么?
- 我们总不能把所有的动态代理全部明文写到代码中啊,这咋办?
- 这时候祭出我们在A001篇讲的
Feature.beforeAnalysis
方法 - 在这里直接手动注册Mapper,反射信息也加上,执行结果如下
- 等等!!为什么他喵的传参数的方式又行了?
- 因为我们在
Feature.beforeAnalysis
方法中注册了动态代理类,在NativeImage静态分析阶段前就已经生成了,所以也不需要在运行时动态代理 - 这下终于写完了…
总结
- NativeImage中的动态代理想了好久怎么去写,中间还插播了B001篇讲
@TargetClass
,涉及的东西稍微有点多,我们最终目的是把MyBatis带到GraalVM世界里(呲个牛逼) - 代码会传到gitee
- 觉得还可以的可以关注下,会持续更新的,大概每周一篇