无法创建自定义类加载器

时间:2010-08-02 21:12:57

标签: java classloader

我正在尝试创建一个自定义类加载器来完成以下任务:

我有一个包com.company.MyClass

的课程

当要求类加载器以下列格式加载任何内容时:

com.company.[additionalPart].MyClass

我希望类加载器加载com.company.MyClass,有效地忽略包名称的[additionalPart]。

这是我的代码:

public class MyClassLoader extends ClassLoader {   

    private String packageName = "com.company.";
    final private String myClass = "MyClass";

    public MyClassLoader(ClassLoader parent) {
        super(parent);
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        Class<?> clazz = null;
        String className = name;

        // Check if the class name to load is of the format "com.company.[something].MyClass 
        if (name.startsWith(packageName)) {
            String restOfClass = className.substring(packageName.length());
            // Check if there is some additional part to the package name
            int index = restOfClass.indexOf('.');
            if (index != -1) {
                restOfClass = restOfClass.substring(index + 1);
                //finally, check if the class name equals MyClass 
                if (restOfClass.equals(myClass)) {
                    // load com.company.MyClass instead of com.company.[something].MyClass
                    className = packageName + myClass;
                    clazz = super.loadClass(className, true);
                }
            }
        }

        if (clazz == null) {
            // Normal clase: just let the parent class loader load the class as usual 
            clazz = super.loadClass(name);
        }

        return clazz;
    }

}

这是我的测试代码:

public class TestClassLoader {

    public void testClassLoader () throws Exception {
        ClassLoader loader = new MyClassLoader(this.getClass().getClassLoader());
        clazz = Class.forName("com.company.something.MyClass", true, loader );       
    }

    public static void main (String[] args) throws Exception {
        TestClassLoader tcl = new TestClassLoader();
        tcl.testClassLoader();
    }
}

MyClassLoader选择正确的类(即com.company.MyClass)并从loadClass正确地返回它(我已经完成了代码),但是,它会在稍后的某个时间点(即之后)抛出异常从JVM调用loadClass,如下所示:

  

线程“main”中的异常   抛出java.lang.ClassNotFoundException:   com / company / something / MyClass at   java.lang.Class.forName0(母语   方法)

     

在   java.lang.Class.forName(Class.java:247)     在[我的代码]

现在,我意识到你们中的一些人可能在想“为什么有人需要这样做?必须有更好的方法”。我确信有,但这对我来说是一种教育,因为我想了解类加载器的工作原理,并深入了解jvm类加载过程。所以,如果你能忽视程序的无聊,并且幽默我,我会非常感激。

2 个答案:

答案 0 :(得分:1)

您的问题

这是纯粹的猜测。班级名称存储在java byte code中。因此,您通过此技术加载的类将是有缺陷的。这可能会使系统混乱。

ClassLoader可能保留对com.company.something.MyClass的引用,但类本身可能保留对com.company.MyClass的引用。 (我使用的可能很多,因为我确实不确定。)可能一切正常,直到你使用MyClass类为止。然后不一致会造成麻烦。那么什么时候抛出这个异常?

如果您有兴趣了解类加载器的工作原理,那么您可以使用 javap 来获取字节代码。这也可以让你检查我的假设。

如果我的假设是正确的,那么解决方案就是修复字节码。有几个软件包允许您设计字节代码。复制一个类,更改复制的类的名称,然后加载它。

<强>除了

虽然与您的问题无关:我发现以下内容不必要复杂(并且它不适用于com.company.something.somethingelse.MyClass)。

        // Check if the class name to load is of the format "com.company.[something].MyClass 
    if (name.startsWith(packageName)) {
        String restOfClass = className.substring(packageName.length());
        // Check if there is some additional part to the package name
        int index = restOfClass.indexOf('.');
        if (index != -1) {
            restOfClass = restOfClass.substring(index + 1);
            //finally, check if the class name equals MyClass 
            if (restOfClass.equals(myClass)) {
                // load com.company.MyClass instead of com.company.[something].MyClass
                className = packageName + myClass;
                clazz = super.loadClass(className, true);
            }
        }

为什么不呢?

//Check if the class name to load is of the format "com.com        // Check if the class name to load is of the format "com.company.[something].MyClass"
if ( ( name . startsWith ( packageName ) ) && ( name . endsWith ( myClass ) ) )

答案 1 :(得分:1)

我认为你不能通过类加载器真正做到这一点。从理论上讲,如果其他一些类试图加载一个它假定的类叫做'com.mycompany.foo.MyClass'那么为时已晚,有人已经有一个带有字节码引用'com.mycompany.foo'的类,并且该类已经加载

通过使用ASM之类的东西在构建时重新打包所有代码,静态级别的重新打包变得更加容易。你当然必须修改他自己的类包本身以及我引用该包的所有类。

如果你使用Maven,请查看shade插件。如果不是,我似乎回想起一个名为JarJar的工具。

您当然可以通过javaagent和ClassTransformer在运行时进行这种字节码操作。 maven-shade-plugin的代码实际上非常小 - 如果你抓住它并撕掉maven部分你可能会在2-3天内工作。