【问题标题】:Using a custom ClassLoader to override existing implementations of classes使用自定义 ClassLoader 覆盖现有的类实现
【发布时间】:2021-05-29 01:10:49
【问题描述】:

首先,我知道以前有人问过这个问题,但我见过的解决方案都没有为我工作!

为了能够用新的字节码覆盖类的现有实现,我想要一个类加载器(设置为默认值),其中对任何类的所有调用(不包括像“java”这样的 java 包)都通过我的 loadClass/findClass 被覆盖方法。从那里我可以检查静态注册表以查看该类是否有覆盖。我愿意尝试以不同的方式执行此操作,但到目前为止,这似乎是最快、最简单的解决方案。

我的问题是,出于某种原因,即使我在 VM args(-Djava.system.class.loader=my.package.path.ProxyClassLoader) 中将加载器设置为默认值,它也会将加载(类)抛给父级,然后我假设将类加载器设置为自身。这意味着所有查找或加载类的调用现在都通过父类而不是我的类加载器。

另外,我想使用 JUnit 来测试它,但我无法让它使用自定义类加载器(在 gradle 中使用带有 JUnit 引擎的 JUnit API)所以我刚上老学校并使用了 main(我在哪里可以修饰 vm 参数)。

我尝试过的事情:

  1. 将类加载器设置为线程上下文加载器。
  2. 重写 loadclass 以尽量不将负载交给父级,这导致的问题比解决的问题多得多。
  3. 尝试进行反射以更改类的类加载器(Java 明确不允许这样做 + 它会非常缓慢且不值得)

这是我的类加载器:

public class ProxyClassLoader extends ClassLoader {
    public ProxyClassLoader(ClassLoader parent) {
        super(parent);
        Thread.currentThread().setContextClassLoader(this);

    }

    //Didn't look like i need to override anything so 
    //this is just a simplicity thing for me(i call it through other classes)
    public Class<?> defineClass(byte[] b, String name) {
        return super.defineClass(name, b, 0, b.length, defaultDomain);
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (ClassManager.hasOverload(name)) return ClassManager.retrieve(name);
        return super.loadClass(name, resolve);

    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return this.loadClass(name, true);
    }
}

谢谢!

【问题讨论】:

    标签: java classloader bytecode


    【解决方案1】:

    不要完全跳过超类加载器。试试这样的?

     @Override protected Class<?> loadClass(String name, boolean resolve) throws 
         ClassNotFoundException {
                if(name belongs to java classes) {
                     return super.loadClass(name, resolve);
                }
                byte[] b = ..// read bytes from your .class files. or use getResourceAsStream(name)
                Class<?> c = defineClass(b, name);
                if(resolve) resolveClass(c);
                return c;
            }
    

    【讨论】:

    • 您好,感谢您的回复!我已经通过以下方式实现了它(pastebin.com/07qfxfAW)但我仍然遇到错误。这似乎可以工作,但是每当点击objectweb.asm classes(我需要)时,它都会抛出一个错误(没有什么特别的,只是说 gradle 任务失败了。)。
    • 我认为这不是特定于该库的,但是我提供的 sn-p 有什么问题吗?我尝试将 objectweb 包添加到我排除的包中(以及任何 java. 包),但它只是遇到了一个新问题。除此之外,如果我只做我的包裹,它就可以了!不过,我需要能够做任何事情。
    • 看起来你的覆盖中唯一缺少的东西是Class&lt;?&gt; c = findLoadedClass(name);。我认为您正在使用ClassManager.hasOverload(name) 处理它,对吗?还要检查你的代码加载的类上设置了什么类加载器。 'c.getClassLoader()'。注意:由两个不同的加载器加载的同一个类被认为是不同的。
    • 非常感谢!我想我大部分都想通了!
    猜你喜欢
    • 2019-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    • 1970-01-01
    • 1970-01-01
    • 2017-11-21
    • 2014-12-26
    相关资源
    最近更新 更多