Gradle 历险记(四):正确的打包jar的方式
gradle 自身不提供打包 jar 的功能,后人各种自定义、乱写、以讹传讹,导致经常打包出来的 jar 都不能用,本文介绍三种方式,一种存在风险的方式,两种正确的方式。
一、存在风险的打包方式——修改 task jar来生成fatjar
这是网上很常见的打包方式,一搜一大片,大概长这样:
1 | jar { |
原理就是执行jar这个task时,将所有的依赖全部解压出来,跳过特定文件名的文件。
使用方法:gradle build
,在build/libs/ 里生成文件,正常情况下不会出问题,编译出来的 jar 里包含了无数个class和资源文件,但是!!!!! 可能会在特定情况下出现问题!可能会在特定情况下出现问题!可能会在特定情况下出现问题! 因为这个逻辑太简单了,是强行解压所有的文件,在依赖非常多的情况下,可能资源文件会相互覆盖(实测的时候,会出现大量的同名文件)。
这种情况我只触发过一次,在某个项目里,使用gradle2.14稳定复现,gradle4.1就没问题,因为依赖之间打起来了,导致 log 无法打出来。 所以如果使用这种方法的话,一定要心里有 B 数,尽量用新版的gradle。 当然这里也有其他的写法,有种使用classpath和原版jar的应该是没啥问题的。
二、使用shadow plugin 生成 fatjar
插件地址: https://github.com/johnrengelman/shadow 这是个大佬写的轮子,应该是maven时代 maven-shade-plugin 的 gradle 版本。
1 | apply plugin: 'com.github.johnrengelman.shadow' |
使用方法 gradle shadowJar ,在build/libs 里生成文件。目前运行稳定,没遇到什么问题。 原理没有仔细看,结果就是个 fatjar,应该是经过一点处理的,否则也会像第一个方案一样,因为冲突导致依赖挂掉。
三、使用springboot plugin 生成带有spring启动器的jar(强烈推荐)
1 | buildscript { |
虽然说SpringBoot常常拿来写 web,但它还是一个很好用的打包工具,不会引入过多的乱七八糟的东西。 使用方式是gradle build ,打出来的是有特定结构的 jar,四部分,本项目的class文件、依赖项目的 jar 包、Manifest、Spring 启动器。 这个很稳,而且配置也超简单,强烈推荐!
四、jar 包运行大致流程
(这部分是根据表现推断的,没有阅读过相关代码)
- 访问 jar 包的 manifest 文件,寻找其中的 'Main-Class',再寻找public static void main(String args[])
- 访问 jar 包的 manifest 文件,获取其中的 'Class-Path',作为可能存放代码的地方
- 执行代码,遇到没见过的class就去放 class 的地方找,再到classpath里找,找不到的话就报错