从自定义 ClassLoader 添加或删除特定的 jar

Add or remove specific jars from custom ClassLoader

我想创建 以在受控环境中执行 JSR223 脚本但失败了,

我正在尝试 remove/add 使用当前(父)类加载器的罐子,我尝试了解决方案 Dynamically removing jars from classpath

public class DistributionClassLoader extends ClassLoader {
    public DistributionClassLoader(ClassLoader parent) {
        super(parent);
    }
    private Map<String, ClassLoader> classLoadersByDistribution =
            Collections.synchronizedMap(new WeakHashMap<>());
    private final AtomicReference<String> distribution = new AtomicReference<>();
    @Override
    protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException {
        final ClassLoader delegate = classLoadersByDistribution.get(distribution.get());
        if (delegate != null) return Class.forName(name, true, delegate);
        throw new ClassNotFoundException(name);
    }
    public void addDistribution(String key, ClassLoader distributionClassLoader){
        classLoadersByDistribution.put(key,distributionClassLoader);
    }
    public void makeDistributionActive(String key){distribution.set(key);}
    public void removeDistribution(String key){
        final ClassLoader toRemove = classLoadersByDistribution.remove(key);
    }
}

但它没有包括我所有的罐子,在测试这个工作

ClassLoader cl = this.getClass().getClassLoader();
Class cls = cl.loadClass("org.springframework.http.HttpStatus");

但是使用解决方案没有找到class

ClassLoader cl = new DistributionClassLoader(this.getClass().getClassLoader());
Class cls = cl.loadClass("org.springframework.http.HttpStatus");

异常:

java.lang.ClassNotFoundException: org.springframework.http.HttpStatus
    at com.DistributionClassLoader.loadClass(DistributionClassLoader.java:24)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

我如何select特定 jar 来添加或从 ClassLoader 中删除?

编辑

我可以使用 @czdepski answer 加载 jar,但我仍然想删除 all/most classes 除了 JDK 的

Method sysMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
sysMethod.setAccessible(true);
sysMethod.invoke(sysLoader, new Object[]{url});

你弄错了代表团。如果它有这个 class.

,你永远不会检查父 class 加载器

如果我们查看 ClassLoader.loadClass(String,boolean)Javadoc,我们会发现:

Loads the class with the specified binary name. The default implementation of this method searches for classes in the following order:

  1. Invoke findLoadedClass(String) to check if the class has already been loaded.

  2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built into the virtual machine is used, instead.

  3. Invoke the findClass(String) method to find the class.

If the class was found using the above steps, and the resolve flag is true, this method will then invoke the resolveClass(Class) method on the resulting Class object.

Subclasses of ClassLoader are encouraged to override findClass(String), rather than this method.

您确实覆盖了 loadClass,但没有对其父类加载器进行任何委托。
相反,您调用 classLoadersByDistribution.get(distribution.get());,这很可能是 null(很难说,但总是期望 WeakHashMap.get() 到 return null)。

如果 delegate 不为空,则您尝试从那里加载 class。这意味着加载的 class 不会使用您的 ClassLoader 来加载新的 classes,而是使用您委托给的 ClassLoader。

毕竟,这听起来像 XY Problem。您想使用脚本 API 执行一些代码并以某种方式控制环境。
您是否尝试使用 SecurityManager


关于您的评论,您需要自己 ClassLoader 来创建 ScriptEngineManager: This ClassLoader is used to search for ScriptEngineFactory implementations. This is done using a service provider interface
如果你不使用自己的脚本引擎,这对你来说应该无关紧要。


如果您的目标是添加几个 jar 以便引擎可以使用它,请创建一个新的 URLClassLoader 并将平台 class 加载器作为父级。 (或扩展 class 加载程序,取决于 java 版本。)
将此 ClassLoader 设置为 Thread.setContextClassLoader() 并创建 ScriptEngine.

如果您确实选择了 URLClassLoader 的父级,它将看不到 class 应用程序 class 加载程序可加载的内容。