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,此文件很重要,原來什麼樣就什麼樣即可,不需要改變,所以忽略
特此記錄一下,互相學習!