使用JShell实例共享动态加载的类 [英] Sharing dynamically loaded classes with JShell instance

查看:92
本文介绍了使用JShell实例共享动态加载的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请查看下面的修改

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

I'm trying to create a JShell instance that gives me access to, and lets me interact with objects in the JVM it was created in. This works fine with classes that have been available at compile time but fails for classes that are loaded dynamically.

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
    }
}

此外,交换 DirectExecutionControl LocalExecutionControl 给出相同的结果,但我不明白这两个类之间的区别。

Additionally, exchanging DirectExecutionControl with LocalExecutionControl gives the same results, but I do not understand the difference between the two classes.

如何将运行时加载的类提供给 JShell实例

How would I make classes that are loaded at runtime available to this JShell instance?

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

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"
    }
}

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

This is the expected output, if the JVM and the JShell instance do not share any memory, however adding com.example.test.C directly to the project instead of loading it dynamically changes the results as follows:

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"

为什么 JVM 之间的内存与 JShell实例没有在运行时加载的类共享?

Why is the memory between the JVM and the JShell instance not shared for classes loaded at runtime?

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

在上例中执行以下代码:

Executing the following code in the context of the above example:

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实例而不再重新加载它?如果不是,为什么已加载静态加载的类?

This shows, that the same class, com.example.test.C is loaded by two different classloaders. Is it possible to add the class to the JShell instance without loading it again? If no, why is the statically loaded class already loaded?

推荐答案

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

The solution is to create a custom LoaderDelegate implementation, that supplies instances of already loaded classes instead of loading them again. A simple example is to use the default implementation, DefaultLoaderDelegate (source) and override the findClass method of its internal RemoteClassLoader

@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实例,请使用以下代码

To create a working JShell instance, use the following code

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

这篇关于使用JShell实例共享动态加载的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆