本文记录内容:Gradle 编译,打 jar 包的时候如果遇到有依赖库只有本地 jar 包,不提供在线仓库依赖的时候,如何把所有依赖打包在一起,附带自己的源码一起上传到 maven 仓库
Gradle: 4.10
Java: 1.8
buildscript {
ext {
nexusConfig = [
"repository" : "https://repo.xxxx",
"uploaderName" : "xxx",
"uploaderPassword": "xxxxxx",
"readerName" : "xxx",
"readerPassword" : "xxxxxx"
]
}
dependencies {classpath "com.github.jengelman.gradle.plugins:shadow:4.0.4"}
repositories {
jcenter()
}
}
subprojects {
apply plugin: 'java'
sourceCompatibility = 1.8
group 'com.xxxx.xxxxxx'
repositories {
jcenter()
mavenCentral()
flatDir { dirs 'libs' }
maven {
url nexusConfig.repository
credentials {
username nexusConfig.readerName
password nexusConfig.readerPassword
}
}
}
dependencies {
testImplementation 'junit:junit:4.12'
}
}
Project 配置文件
version '1.1.2'
description 'xiaomi push service'
dependencies {
implementation fileTree('libs')
}
apply from: "../publish.gradle"
apply from: "../shadow.gradle"
如上, 这是一个使用小米推送的服务端业务封装库,小米只提供了本地 jar 包,需要打包在一起供其他项目依赖。
插件地址
shadow.gradle 的内容
apply plugin: 'com.github.johnrengelman.shadow'
shadowJar {
// 完整名称为 baseName-version-classifier.jar
baseName = project.name
// 默认为 '-all' 为 null 则去除该参数
classifier = null
version = project.version
// 方法数超过 65535 会报错, 需要打开下面这个配置
//zip64 = true
// 去除和添加文件 META-INF
// include '.... 文件'
// exclude '... 文件'
// 如果有 Main 函数, 如下配置启动类
//manifest {
// attributes 'Main-Class': 'com.example.Main'
//}
}
sourcesJar.dependsOn(shadowJar)
打 fatJar 有好几种方式,Shadow 用的是第二种
优点:
缺点:
插件会创建 2 个 task, shadowJar 负责打包, uploadShadow 负责上传 Maven,核心代码就是下面这一段了
protected void configureShadowTask(Project project) {
JavaPluginConvention convention = project.convention.getPlugin(JavaPluginConvention)
ShadowJar shadow = project.tasks.create(SHADOW_JAR_TASK_NAME, ShadowJar)
shadow.group = SHADOW_GROUP
shadow.description = 'Create a combined JAR of project and runtime dependencies'
shadow.conventionMapping.with {
map('classifier') {
'all'
}
}
if (GradleVersion.current() >= GradleVersion.version("5.1")) {
shadow.archiveClassifier.set("all")
}
shadow.manifest.inheritFrom project.tasks.jar.manifest
shadow.doFirst {
def files = project.configurations.findByName(ShadowBasePlugin.CONFIGURATION_NAME).files
if (files) {
def libs = [project.tasks.jar.manifest.attributes.get('Class-Path')]
libs.addAll files.collect { "${it.name}" }
manifest.attributes 'Class-Path': libs.findAll { it }.join(' ')
}
}
shadow.from(convention.sourceSets.main.output)
shadow.configurations = [project.configurations.findByName('runtimeClasspath') ?
project.configurations.runtimeClasspath : project.configurations.runtime]
shadow.exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'module-info.class')
project.artifacts.add(ShadowBasePlugin.CONFIGURATION_NAME, shadow)
configureShadowUpload()
}
private void configureShadowUpload() {
configurationActionContainer.add(new Action() {
void execute(Project project) {
project.plugins.withType(MavenPlugin) {
Upload upload = project.tasks.withType(Upload).findByName(SHADOW_UPLOAD_TASK)
if (!upload) {
return
}
upload.configuration = project.configurations.shadow
MavenPom pom = upload.repositories.mavenDeployer.pom
pom.scopeMappings.mappings.remove(project.configurations.compile)
pom.scopeMappings.mappings.remove(project.configurations.runtime)
pom.scopeMappings.addMapping(MavenPlugin.RUNTIME_PRIORITY, project.configurations.shadow, Conf2ScopeMappingContainer.RUNTIME)
}
}
})
}
上面的代码就是配置 task 实现了 jar 包位置替换, 指定 Class-Path
,移除多余文件,去除 Maven 中的打包任务替换为自己的然后上传
更多代码看 GitHub
按照第一节的内容,已经完成了打包 fatJar 并上传 Maven 的操作,但我发现使用 uploadShadow 这个 Shadow 插件自带的 task 上传的 jar 包里,不带源码!不爽,看看能不能改进。
实现方案如上述标题,默认的 Maven 插件提供的 task 中会调用 jar 这个 task 进行打包,如果有指定源码,那么也会同步上传源码,uploadShadow 中把实现给替换了,但没有写入上传源码的操作,就算打出源码也不会上传,所以换种思路。
apply plugin: 'maven'
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.java.srcDirs
}
artifacts {
archives sourcesJar
}
// 如果希望 gradle install,安装到. m2 本地仓库,参考下面的内容
install {
repositories.mavenInstaller {
pom.project {
version project.version
artifactId project.name
groupId project.group
packaging 'jar'
description project.description
}
}
}
uploadArchives {
configuration = configurations.archives
repositories {
mavenDeployer {
repository(url: nexusConfig.repository) {
authentication(
userName: nexusConfig.uploaderName,
password: nexusConfig.uploaderPassword
)
}
snapshotRepository(url: nexusConfig.repositorySnapshot) {
authentication(
userName: nexusConfig.snapshotName,
password: nexusConfig.snapshotPassword
)
}
pom.project {
version project.version
artifactId project.name
groupId project.group
packaging 'jar'
description project.description
}
}
}
}
上面这个 publish.gradle 是无 shadow 依赖的, 只是加入一个全局的 sourceJar 任务用来生成源码。
configurations.archives 是 uploadArchives 任务的默认实现,会上传源码,因此我们只要让 sourceJar 和 shadowJar 这 2 个任务都运行一遍就可以了。
只要 shadow.gradle 在 publish.gradle 之后加载,它就能拿到 publish.gradle 中定义的 sourceJar 任务,并指定这个任务依赖 shadowJar 运行,uploadArchives 在进行编译源码操作时会连带先调用 shadowJar 编译 fatJar 替换掉 jar 任务的编译结果,最后一起上传,简单并解耦的实现了打包 fatJar 并带源码上传 Maven 的需求
apply from: "../publish.gradle"
apply from: "../shadow.gradle"
shadow.gradle
apply plugin: 'com.github.johnrengelman.shadow'
shadowJar {
.....
}
sourcesJar.dependsOn(shadowJar)
Maven就是xml丑了点,其实配置很简单
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-shade-pluginartifactId>
<version>3.2.0version>
<executions>
<execution>
<goals>
<goal>shadegoal>
goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${app.main.class}Main-Class>
<X-Compile-Source-JDK>${maven.compile.source}X-Compile-Source-JDK>
<X-Compile-Target-JDK>${maven.compile.target}X-Compile-Target-JDK>
manifestEntries>
transformer>
transformers>
configuration>
execution>
executions>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-source-pluginartifactId>
<configuration>
<attach>trueattach>
configuration>
<executions>
<execution>
<phase>verifyphase>
<goals>
<goal>jargoal>
goals>
execution>
executions>
plugin>
plugins>
build>
替换${app.main.class}为你的main函数所在的类
${maven.compile.source}是你的项目源码的JDK版本如1.8
${maven.compile.target}是你的项目编译的Java版本如1.8