1. 程式人生 > >Gradle生成可執行jar包(三)

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;
        }
    }
}