问题描述
我正在尝试使用javassist加载外部jar文件并在运行时调用其main方法,但是当我尝试使用以下代码进行此操作时:
File file = new File("C:\\Users\\MainPC\\Desktop\\test.jar");
Classpool cp = Classpool.getDefault();
cp.insertClasspath(file.getAbsolutePath());
Class<?> MainClass = cp.get("TestPackage.MainClass").toClass();
MainClass.getmethod("main",String[].class).invoke(null,new Object[] {args});
它引发以下异常:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at ReflectionTests.main(ReflectionTests.java:99)
Caused by: java.lang.NoClassDefFoundError: TestPackage/OtherClass
at TestPackage.MainClass.main(UnkNown Source)
... 5 more
Caused by: java.lang.classNotFoundException: TestPackage.OtherClass
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.classLoader.loadClass(ClassLoader.java:522)
... 6 more
当我仅使用内置的Java反射api尝试相同的操作时,它没有任何问题:
File file = new File("C:\\Users\\MainPC\\Desktop\\test.jar");
urlclassloader cl = new urlclassloader(new URL[] {new URL("jar:file:"+file.getAbsoluteFile()+"!/")});
Class<?> clazz = cl.loadClass("TestPackage.MainClass");
clazz.getmethod("main",new Object[] {args});
(上面没有抛出异常,并按预期方式调用了jar文件的main方法)
这使我相信我在javassist中做错了(特别是在加载jar文件类时)。有人可以告诉我这是什么吗? 我应该提到jar文件仅包含2个类:MainClass.class和OtherClass.Class。两者都驻留在一个名为TestPackage的程序包中。似乎该错误与javassist加载MainClass类时无法找到OtherClass类有关。
解决方法
问题在于,当我调用ct.toClass()
时,它仅将类本身公开给我的运行时类加载器(而不是整个类池的类路径)。然后,当我稍后尝试调用此类的主要方法时,我的运行时类加载器将尝试执行加载显然不知道的另一个类的部分,并因此引发 ClassNotFoundException 。>
解决方案是使用javassist提供的类加载器(javassist.Loader),该类加载器将类池作为构造函数中的参数,然后能够从类池的类路径中正确加载和解析类。
这是我要实现的工作代码示例:
File file = new File("C:\\Users\\MainPC\\Desktop\\test.jar");
ClassPool cp = ClassPool.getDefault();
cp.insertClassPath(file.getAbsolutePath());
Loader loader = new Loader(cp);
Class<?> MainClass = loader.loadClass("TestPackage.MainClass");
MainClass.getMethod("main",String[].class).invoke(null,new Object[] {args});