Sharing dynamically loaded classes with JShell instance

限于喜欢 提交于 2019-12-02 17:34:14

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

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

only speaking to a small part of this rather substantial question:

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

LocalExecutionControl extends DirectExecutionControl and it overrides only invoke(Method method), the bodies of which are ...

local:

    Thread snippetThread = new Thread(execThreadGroup, () -> {
            ...
            res[0] = doitMethod.invoke(null, new Object[0]);
            ...
    });

direct:

    Object res = doitMethod.invoke(null, new Object[0]);

so the difference between the two classes is that direct invokes the method in the current thread, and local invokes it in a new thread. the same classloader is used in both cases, so you'd expect the same results in terms of sharing memory and loaded classes

Now, there is better and easier solution:

package ur.pkg;

import jdk.jshell.JShell;
import jdk.jshell.execution.LocalExecutionControlProvider;

public class TestShell {
    public static int testValue = 5;

    public static void main(String[] args) {
        JShell shell = JShell.builder().executionEngine(new LocalExecutionControlProvider(), null).build();
        TestShell.testValue++;
        System.out.println(TestShell.testValue);
        shell.eval("ur.pkg.TestShell.testValue++;").forEach(p -> {
            System.out.println(p.value());
        });
        System.out.println(TestShell.testValue);

    }

}

Default execution engine is JDI, but u can switch it to local or own.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!