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 mons.lang3.ObjectUtils;import mons.lang3.RandomUtils;import mons.lang3.StringUtils;import mons.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.InvocationTargetExceptionat 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/StringUtilsat test3.Test1.test(Test1.java:14)... 5 moreCaused by: java.lang.ClassNotFoundException: mons.lang3.StringUtilsat .URLClassLoader.findClass(Unknown Source)at java.lang.ClassLoader.loadClass(Unknown Source)at java.lang.ClassLoader.loadClass(Unknown Source)... 6 more
找了好久都没有解决这个问题,最后发现,在jar中随便一个类中写一个main方法,然后打包成可执行jar,虽然是可执行jar但是不需要直接执行的,然后就可以了:
执行testtruetruefalsetruetruefalseC:\Program Files\Java\jre1.8.0_191null
如果需要在可执行jar的根目录添加一些文件,可以进行如下操作:
将打包的jar解压成文件夹:tt,然后将需要的文件复制到根目录
执行如下命令:
jar cvfM tt.jar -C tt\ .
cvfM中的M是为了忽略MANIFEST.MF,此文件很重要,原来什么样就什么样即可,不需要改变,所以忽略
特此记录一下,互相学习!