Gradle生成可运行jar包(三)

  在 https://blog.csdn.net/zero__007/article/details/80428632 中介绍了生成可运行jar包的方式,来想想,能不能把依赖jar包也打包进入jar包,注意不是把第三方jar包的源码打进去。
  事实是,是可以的,下面来介绍这个奇技淫巧。主要思路是使用ClassLoader来实现。首先是打jar包:

jar {
    manifest {
        attributes "Implementation-Title": project.name
        attributes "Implementation-Version": '1.0.0'
        attributes 'Main-Class': 'org.zero.jar.ResourceLoadFromJarUtil'
    }

    into('lib'){
        from configurations.runtime
    }
}

  我们知道java中双亲委派模式,我们是不是可以把在jar包里面的第三方jar包解出来,然后用URLClassLoader加载一下,再把这个URLClassLoader的parent设置为Thread.currentThread().getContextClassLoader(),再把这个URLClassLoader通过Thread.currentThread().setContextClassLoader()绑到线程上,不就OK了吗?诚然,这种方式我们可以使用loadClass(“xxx.xxx.xxx”)的方式来加载到某个类,但是我们无法使用XXX xxx = new XXX()的方式来初始化第三方的类。
  我们知道java中最基本的ClassLoaderAppClassLoader,它是默认的系统加载器,这个ClassLoader实际上也是URLClassLoader的子类。当使用XXX xxx = new XXX()的方式加载类时使用的就是这个,我们是不是可以通过addURL方法把我们的第三方jar加到这个AppClassLoader呢?答案是可以的:

public class ResourceLoadFromJarUtil {
    public static void main(String[] args) {
        Map<String, String> map = Maps.newHashMap();
        map.put("hello", "world");
        System.out.println(map);
    }

    static {
        loadJarsInJar();
    }

    private static void loadJarsInJar() {
        // 非java -jar 启动时,java.class.path中包含 rt.jar 等核心jar,因此依据这个来判断是否需要addURL
        String[] jars = System.getProperty("java.class.path").split(":");
        for (String jar : jars) {
            if (jar.endsWith("rt.jar")) {
                return;
            }
        }

        List<File> fileList = new ArrayList<>();
        for (String jar : jars) {
            if (!jar.endsWith(".jar")) {
                return;
            }
            try (JarFile jarFile = new JarFile(jar)) {
                for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
                    JarEntry jarEntry = e.nextElement();
                    if (jarEntry.getName().endsWith("jar")) {
                        File f = convert(jarEntry.getName(), jarFile.getInputStream(jarEntry));
                        fileList.add(f);
                    }
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        // Use reflection
        try {
            URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
            Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            method.setAccessible(true);
            for (File file : fileList) {
                method.invoke(classLoader, file.toURI().toURL());
                file.deleteOnExit();
            }
            Thread.currentThread().setContextClassLoader(classLoader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static File convert(String name, InputStream inputStream) {
        try {
            File file = new File(name);
            OutputStream os = new FileOutputStream(file);
            int bytesRead;
            byte[] buffer = new byte[8192];
            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            return file;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

参考:https://stackoverflow.com/questions/252893/how-do-you-change-the-classpath-within-java

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值