动态加载后调用时ClassNotFoundException

时间:2018-03-06 11:25:45

标签: java

我正在尝试使应用程序动态加载某些类然后调用启动方法,但问题是由于不同的ClassLoader,一个类无法调用另一个类的方法,但是当我已经使用Google搜索时,我创建了两个带有父级的类加载器。这是我的测试类:

public class Plugin {
    public static void main(String[] args) throws Exception {
        Class<?> fii = loadClass(new File("Fii.class"), "ua.i0xhex.plugin.Fii");
        Class<?> goo = loadClass(new File("Goo.class"), "ua.i0xhex.plugin.Goo");
        goo.getMethod("hello", new Class<?>[0]).invoke(goo.newInstance(), (Object[]) null);
    }

    public static Class<?> loadClass(File file, String name) throws Exception {
        DLoader loader = new DLoader(Plugin.class.getClassLoader());
        byte[] data = toByte(file);
        Class<?> clazz = loader.defineClass(name, data);
        return clazz;
    }

    public static byte[] toByte(File file) throws Exception {
        FileInputStream inputStream = new FileInputStream(file);
        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        int count;
        byte[] bytes = new byte[1024];
        while ((count = inputStream.read(bytes, 0, bytes.length)) != -1) 
            byteOutputStream.write(bytes, 0, count);
        inputStream.close();
        return byteOutputStream.toByteArray();
    }

    public static class DLoader extends ClassLoader {
        public DLoader(ClassLoader parentLoader) {
            super(parentLoader);
        }
        public Class<?> defineClass(String name, byte[] b) {
            return super.defineClass(name, b, 0, b.length);
        }
    }
}

我已经在我的测试应用附近编译和复制了两个类。 Goo.hello()必须打印“Hello World”,然后调用Fii.hi()

输出:

Hello World!
Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at ua.i0xhex.plugin.Plugin.main(Plugin.java:11)
Caused by: java.lang.NoClassDefFoundError: ua/i0xhex/plugin/Fii
    at ua.i0xhex.plugin.Goo.hello(Goo.java:11)
    ... 5 more
Caused by: java.lang.ClassNotFoundException: ua.i0xhex.plugin.Fii
    at java.lang.ClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 6 more

如何确保我的动态加载类能够调用另一个加载类的方法?如果我为这两个课程制作1 ClassLoader,那么一切都有效,但在实际工作中不仅会有一个。

2 个答案:

答案 0 :(得分:1)

每次调用它时,loadClass方法都会定义一个新的类加载器

 DLoader loader = new DLoader(Plugin.class.getClassLoader());

您使用该类加载器加载您的类。通过这种方式,您可以获得一个用于Fii的类加载器和一个用于Goo的类加载器,具有相同的父类加载器。

你可能想要两个相同的类加载器,所以你可能想把它作为参数传递给你的loadclass方法,如下所示:

public static void main(String[] args) throws Exception {
    DLoader loader = new DLoader(Plugin.class.getClassLoader())
    Class<?> fii = loadClass(loader, new File("Fii.class"), "ua.i0xhex.plugin.Fii");
    Class<?> goo = loadClass(loader, new File("Goo.class"), "ua.i0xhex.plugin.Goo");
    goo.getMethod("hello", new Class<?>[0]).invoke(goo.newInstance(), (Object[]) null);
}

public static Class<?> loadClass(DLoader loader, File file, String name) throws Exception {
    byte[] data = toByte(file);
    Class<?> clazz = loader.defineClass(name, data);
    return clazz;
}

如果您更喜欢该路线

,您当然也可以将您的类加载器存储为静态变量

答案 1 :(得分:0)

一个类可以访问由它的类加载器或(任何)父类加载器加载的类。

它可以访问由子类或兄弟类加载器加载的类(您实际上要做的是访问由同级类加载器加载的类,即父类加载器的子类)。

想象一下,您在一台应用服务器上运行了两个应用程序。应用程序服务器使用父类加载器并为不同子类加载器中的每个应用程序加载类。您不希望其中一个应用程序能够从另一个应用程序访问类。