【问题标题】:Add or remove specific jars from custom ClassLoader从自定义 ClassLoader 添加或删除特定 jar
【发布时间】:2019-11-19 17:20:36
【问题描述】:

我想创建dynamically a classloader 用于在受控环境中执行 JSR223 脚本但失败了,

我正在尝试使用当前(父)ClassLoader 删除/添加 jar,我尝试了解决方案 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");

但是使用解决方案找不到类

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)

如何选择特定 jar 以在 ClassLoader 中添加或删除?

编辑

我可以使用 @czdepski answer 加载 jar,但我仍然想删除除 JDK 之外的所有/大多数类

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

【问题讨论】:

  • 你能解释一下为什么你想从类加载器中删除东西吗?因为这不是完全可能的,所以你不能强制卸载类。类可能会使用类加载器进行垃圾收集。请提供一些您需要类似内容的示例。
  • 我不明白这与从类加载器中删除 jars 有什么关系,当执行一个 groovy 脚本时,只需要使用不使用该库的不同类加载器来加载它。所以这样的库不应该是父类加载器的一部分。因为从父类加载器中删除它们无论如何都不会改变任何东西。
  • @gotofinal 如何在现有类加载器的基础上创建类加载器?
  • 检查我在答案中写的内容是否与您需要的类似。

标签: java jar classloader dynamic-class-loaders


【解决方案1】:

你弄错了委托。你永远不会检查父类加载器是否有这个类。

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

加载具有指定二进制名称的类。此方法的默认实现按以下顺序搜索类:

  1. 调用findLoadedClass(String) 来检查类是否已经加载。

  2. 在父类加载器上调用loadClass 方法。如果 parent 为 null,则使用虚拟机中内置的类加载器。

  3. 调用findClass(String) 方法来查找类。

如果使用上述步骤找到了类,并且解析标志为真,则此方法将在生成的 Class 对象上调用 resolveClass(Class) 方法。

鼓励 ClassLoader 的子类覆盖 findClass(String),而不是这个方法。

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

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

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


关于您需要自己的ClassLoader 来创建ScriptEngineManager 的评论:此ClassLoader 用于搜索ScriptEngineFactory 实现。这是使用service provider interface 完成的。
如果您不使用自己的脚本引擎,这对您来说应该没有关系。


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

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

【讨论】:

  • 关于上次commet,我需要ClassLoader,见docs.oracle.com/javase/7/docs/api/javax/script/…
  • 那么您想提供自己的 ScriptEngine 吗?通过你自己的 ClassLoader 加载?
  • 我想在发送到 ScriptEngine 之前使用当前的类加载器更新(添加/删除 jars)
  • 您不应该在创建 ClassLoader 后对其进行修改。这很危险。 (有关加载类,请参阅 JVMS。)
  • 但我想在现有类加载器的基础上使用更安全/可配置的类加载器
猜你喜欢
  • 2021-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多