java动态加载jar时,jar中还有第三方jar无法加载的解决方法
当java插件化开发时,即一个java程序在运行的情况下动态加载另一个jar,网上大多数的方法如下
public static void main(String[] args)
throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
String jarPath = "C:\\Users\\ricozhou\\Desktop\\tt.jar";
File jarFile = new File(jarPath);
String className = "test3.Test1";
URL url = jarFile.toURI().toURL();
ClassLoader system = new URLClassLoader(new URL[] { url }, Thread.currentThread().getContextClassLoader());
Class<?> cs = system.loadClass(className);
Object object = cs.newInstance();
System.out.println(cs.getMethod("test").invoke(object));
}
使用自定义类加载器加载,本来没什么问题,而且示例也都是一些简单的helloworld,但是当tt.jar中引入了第三方jar时,方法test中使用了第三方jar时,则会无法找到第三方类,如:创建一个项目,引入Common lang3
package test3;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
public class Test1 {
public static void main(String[] args) {
System.out.println("执行main");
System.out.println(StringUtils.isEmpty(null));
System.out.println(StringUtils.isEmpty(""));
System.out.println(ObjectUtils.anyNotNull(null, null));
System.out.println(ObjectUtils.anyNotNull(11, null));
System.out.println(RandomUtils.nextBoolean());
System.out.println(RandomUtils.nextBoolean());
System.out.println(SystemUtils.JAVA_HOME);
}
public void test() {
System.out.println("执行test");
System.out.println(StringUtils.isEmpty(null));
System.out.println(StringUtils.isEmpty(""));
System.out.println(ObjectUtils.anyNotNull(null, null));
System.out.println(ObjectUtils.anyNotNull(11, null));
System.out.println(RandomUtils.nextBoolean());
System.out.println(RandomUtils.nextBoolean());
System.out.println(SystemUtils.JAVA_HOME);
}
}
导出为普通jar,注意不是可运行jar,因为这只是一个示例,以后可能这是一个类库,不需要main方法的,所以我都是直接打包成普通jar,这样再使用另一个项目动态加载这个jar,则会报错:
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 test4.Test11.main(Test11.java:21)
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils
at test3.Test1.test(Test1.java:14)
... 5 more
Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.StringUtils
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 6 more
找了好久都没有解决这个问题,最后发现,在jar中随便一个类中写一个main方法,然后打包成可执行jar,虽然是可执行jar但是不需要直接执行的,然后就可以了:
执行test
true
true
false
true
true
false
C:\Program Files\Java\jre1.8.0_191
null
如果需要在可执行jar的根目录添加一些文件,可以进行如下操作:
将打包的jar解压成文件夹:tt,然后将需要的文件复制到根目录
执行如下命令:
jar cvfM tt.jar -C tt\ .
cvfM中的M是为了忽略MANIFEST.MF,此文件很重要,原来什么样就什么样即可,不需要改变,所以忽略
特此记录一下,互相学习!