我需要定义一个ClassLoader,以从各自的类文件中动态重载某些类。这是因为自运行系统启动以来已更改了类。

目前,我有一个类加载器,可以从其文件加载一个类。我进一步需要的是加载该类所依赖的所有类,并且这些类在已更改的类列表中。

简单地说,我有一个ClassLoader类,当所需的类在列表中时,该类会覆盖loadClass方法以加载更改的类文件。否则,它将使用默认(父)类加载器加载类。

是否可以使用相同的类加载器从最初加载的类中(重新)加载类定义中的类?

所以,一个简单的例子

Class A {
    static method() {
        B b = new B();
        C c = new C();
        // do something with B and C
    }
}
Class B {
    public X x = new X();
}


当我用A加载类MyCustomClassLoader时,我将其交给包含AC f.e的类列表。因为我知道这些类的类文件(bin/A.class)已更改。因此,它应该手动从类A加载文件,并且在加载BC时,应检查它们是否在更改的类列表中。如果不是,则可以使用默认的类加载器加载它们,否则将递归地与A相同(因此还要检查B依赖的类)。

我有从文件工作的加载,但没有递归。我也知道,此方法不会全局覆盖Java new运算符。但是我只需要运行一个已更改的类,但是其依赖关系也已更改。

编辑:
请注意,使用Instrumentation无法做到这一点。它的文档引用了以下内容,对于retransformClasses()redefineClasses()均如此。


  重新转换可能会更改方法主体,常量池和属性。重新转换不得添加,删除或重命名字段或方法,更改方法的签名或更改继承。这些限制可能会在将来的版本中取消。


因此,我做了一个工作代理来更改方法主体,但是只有当我注意到它在方法创建或删除时崩溃时,我才找到了这段文字。

最佳答案

我终于让它工作了。我做了以下课程:

我相信这里的关键是resolveClass()方法。尽管文档没有提供任何有用的信息,但我相信此方法可确保在加载类之后,也将加载所有使用的类。

它要求您每次更改的类之一再次更改或新的类更改时都创建一个新类。

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Set;

/**
 * This class provides dynamic loading in the sense that the given class and
 * other changed classes are loaded from disk, not from cache.
 * Ensures most recent version is used for those classes.
 */
public class DynamicClassLoader extends ClassLoader{

    private Set<String> classes;

    public DynamicClassLoader(Set<String> changedClasses) {
        this.classes = changedClasses;
    }

    /**
     * Dynamically loads the class from it's binary file.
     * Classes this class depends on will only be loaded dynamically when
     * they are in list of changed classes given at construction.
     * @throws ClassNotFoundException
     */
    public Class<?> dynamicallyLoadClass(String name)
        throws ClassNotFoundException {
        classes.add(name);
        return loadClass(name);
    }

    /**
     * Finds the class dynamically, contrary to Class.forname which can use
     * cached copies.
     * This means it forces a reload of the class data.
     * Source: http://stackoverflow.com/questions/3971534/
     */
    @Override
    protected Class<?> findClass(String s) throws ClassNotFoundException {
        try {
            byte[] bytes;
            bytes = loadClassData(s);
            return defineClass(s, bytes, 0, bytes.length);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException{
        return loadClass(name, true);
    }

    /**
     * Overridden to not check the parent's loadClass method first.
     */
    @Override
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException {
        Class<?> clazz = findLoadedClass(name);
        if(clazz != null)
            return clazz;

        if(classes.contains(name)) {
            clazz = findClass(name);
            resolveClass(clazz);
            return clazz;
        }
        return super.loadClass(name, resolve);
    }

    /**
     * Load class data from byte code.
     *
     * @param className
     * @return
     * @throws IOException
     */
    protected byte[] loadClassData(String className) throws IOException {
        File f = new File("bin/"
            + className.replaceAll("\\.", "/") + ".class");
        int size = (int) f.length();
        byte buff[] = new byte[size];
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        dis.readFully(buff);
        dis.close();
        return buff;
    }
}

关于java - Java中的递归ClassLoader,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20128538/

10-09 07:12