gpt4 book ai didi

java - 与 JShell 实例共享动态加载的类

转载 作者:IT老高 更新时间:2023-10-28 20:35:47 26 4
gpt4 key购买 nike

请查看下面的修改

我正在尝试创建一个 JShell 实例,让我可以访问并与创建它的 JVM 中的对象进行交互。这适用于在编译时可用但对于动态加载的类失败的类动态

public class Main {

public static final int A = 1;
public static Main M;

public static void main(String[] args) throws Exception {
M = new Main();
ClassLoader cl = new URLClassLoader(new URL[]{new File("Example.jar").toURL()}, Main.class.getClassLoader());
Class<?> bc = cl.loadClass("com.example.test.Dynamic");//Works
JShell shell = JShell.builder()
.executionEngine(new ExecutionControlProvider() {
@Override
public String name() {
return "direct";
}

@Override
public ExecutionControl generate(ExecutionEnv ee, Map<String, String> map) throws Throwable {
return new DirectExecutionControl();
}
}, null)
.build();
shell.eval("System.out.println(com.example.test.Main.A);");//Always works
shell.eval("System.out.println(com.example.test.Main.M);");//Fails (is null) if executionEngine is not set
shell.eval("System.out.println(com.example.test.Dynamic.class);");//Always fails
}
}

此外,将 DirectExecutionControlLocalExecutionControl 交换会得到相同的结果,但我不明白这两个类之间的区别。

如何使 运行时 加载的类可用于此 JShell 实例

编辑:这个问题的第一部分已经解决,下面是更新的源代码来演示问题的第二部分

public class Main {

public static void main(String[] args) throws Exception {
ClassLoader cl = new URLClassLoader(new URL[]{new File("Example.jar").toURL()}, Main.class.getClassLoader());
Class<?> c = cl.loadClass("com.example.test.C");
c.getDeclaredField("C").set(null, "initial");
JShell shell = JShell.builder()
.executionEngine(new ExecutionControlProvider() {
@Override
public String name() {
return "direct";
}

@Override
public ExecutionControl generate(ExecutionEnv ee, Map<String, String> map) throws Throwable {
return new DirectExecutionControl();
}
}, null)
.build();
shell.addToClasspath("Example.jar");
shell.eval("import com.example.test.C;");
shell.eval("System.out.println(C.C)"); //null
shell.eval("C.C = \"modified\";");
shell.eval("System.out.println(C.C)"); //"modified"
System.out.println(c.getDeclaredField("C").get(null)); //"initial"
}
}

这是预期的输出,如果 JVMJShell 实例 不共享任何内存,但是添加 com.example.test.C 直接加载到项目中,而不是动态加载它,结果如下:

shell.eval("import com.example.test.C;");
shell.eval("System.out.println(C.C)"); //"initial"
shell.eval("C.C = \"modified\";");
shell.eval("System.out.println(C.C)"); //"modified"
System.out.println(c.getDeclaredField("C").get(null)); //"modified"

为什么在运行时加载的类不共享 JVMJShell 实例 之间的内存?

编辑 2:问题似乎是由不同的类加载器引起的

在上述示例的上下文中执行以下代码:

System.out.println(c.getClassLoader()); //java.net.URLClassLoader
shell.eval("System.out.println(C.class.getClassLoader())"); //jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader
shell.eval("System.out.println(com.example.test.Main.class.getClassLoader())"); //jdk.internal.loader.ClassLoaders$AppClassLoader

这表明,同一个类 com.example.test.C 由两个不同的类加载器加载。是否可以将类添加到 JShell 实例而不再次加载它?如果不是,为什么静态加载的类已经加载了?

最佳答案

解决方案是创建一个自定义 LoaderDelegate 实现,它提供已加载类的实例,而不是再次加载它们。一个简单的例子是使用默认实现 DefaultLoaderDelegate ( source ) 并覆盖其内部 RemoteClassLoader

findClass 方法
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = classObjects.get(name);
if (b == null) {
Class<?> c = null;
try {
c = Class.forName(name);//Use a custom way to load the class
} catch(ClassNotFoundException e) {
}
if(c == null) {
return super.findClass(name);
}
return c;
}
return super.defineClass(name, b, 0, b.length, (CodeSource) null);
}

要创建一个有效的 JShell 实例,请使用以下代码

JShell shell = JShell.builder()
.executionEngine(new ExecutionControlProvider() {
@Override
public String name() {
return "name";
}

@Override
public ExecutionControl generate(ExecutionEnv ee, Map<String, String> map) throws Throwable {
return new DirectExecutionControl(new CustomLoaderDelegate());
}
}, null)
.build();
shell.addToClasspath("Example.jar");//Add custom classes to Classpath, otherwise they can not be referenced in the JShell

关于java - 与 JShell 实例共享动态加载的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48896013/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com