Gradle生成可執行jar包(三)
阿新 • • 發佈:2019-01-10
在 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中最基本的
ClassLoader
是AppClassLoader
,它是預設的系統載入器,這個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;
}
}
}