如何在JVM宿主代码中访问JSR223 ScriptEngine定义的Kotlin类
问题原因解释
你遇到的ClassNotFoundException本质是类加载器隔离导致的:Kotlin的ScriptEngine执行脚本时,会使用独立的自定义类加载器编译、加载脚本中定义的类,这个类加载器和宿主应用的默认类加载器是父子隔离关系,宿主的Class.forName默认使用当前线程的上下文类加载器(也就是宿主自身的类加载器),自然找不到脚本引擎加载的类。
是否只能通过eval获取类引用?
不是,有两种常用的实现方式,你目前用的eval方案是兼容性最好的首选方案:
方案1:直接通过
eval返回类引用(推荐)
就是你现在正在使用的写法,不需要关心类加载器、脚本类全限定名规则的差异,不同版本的Kotlin脚本引擎都兼容,代码实现最简洁:val fooKClass = scriptEngine.eval("MyFoo::class") as KClass<Foo> val foo = fooKClass.createInstance() foo.bar()方案2:通过脚本引擎的类加载器加载类
JSR223规范中脚本引擎的上下文会暴露自身使用的类加载器,你可以拿到这个类加载器后传给Class.forName,就能加载到脚本中定义的类:// 先获取脚本引擎的类加载器 val scriptClassLoader = scriptEngine.getContext().classLoader // 传入类加载器加载类 val fooKClass = Class.forName("MyFoo", true, scriptClassLoader).kotlin as KClass<Foo> val foo = fooKClass.createInstance() foo.bar()注意:部分版本的Kotlin脚本引擎会对脚本中的顶级类自动加前缀或后缀,如果你用上面的代码还是找不到类,可以先执行
scriptEngine.eval("MyFoo::class.qualifiedName")打印出类的实际全限定名,再替换到Class.forName的第一个参数即可。
关于把脚本类注入宿主类加载器
理论上可以通过修改Kotlin脚本引擎的编译配置,指定脚本生成的类直接注入到宿主的类加载器,但这个属于版本相关的高级特性,兼容性很差,一般业务场景完全没有必要用。
内容的提问来源于stack exchange,提问作者Dan Lugg
相关产品推荐
相关产品推荐

