Gradle 打包依赖为 fatJar 添加源码上传到 Maven

2024-02-21 10:18

本文主要是介绍Gradle 打包依赖为 fatJar 添加源码上传到 Maven,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文记录内容:Gradle 编译,打 jar 包的时候如果遇到有依赖库只有本地 jar 包,不提供在线仓库依赖的时候,如何把所有依赖打包在一起,附带自己的源码一起上传到 maven 仓库

Gradle: 4.10
Java: 1.8

1. 合并本地依赖 jar 包,打包出 fatJar

1.1 首先贴一下项目结构


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.8group 'com.xxxx.xxxxxx'repositories {jcenter()mavenCentral()flatDir { dirs 'libs' }maven {url nexusConfig.repositorycredentials {username nexusConfig.readerNamepassword 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 包,需要打包在一起供其他项目依赖。

1.2 使用 Shadow 插件打 fatJar

插件地址

shadow.gradle 的内容


apply plugin: 'com.github.johnrengelman.shadow'shadowJar {// 完整名称为 baseName-version-classifier.jarbaseName = project.name// 默认为 '-all' 为 null 则去除该参数classifier = nullversion = project.version// 方法数超过 65535 会报错, 需要打开下面这个配置//zip64 = true// 去除和添加文件 META-INF// include '.... 文件'// exclude '... 文件'// 如果有 Main 函数, 如下配置启动类//manifest {//    attributes 'Main-Class': 'com.example.Main'//}
}sourcesJar.dependsOn(shadowJar)

1.3 Shadow 打包原理

打 fatJar 有好几种方式,Shadow 用的是第二种

  1. 解压所有 jar,重新压缩合并在一个 jar 中
  2. 不合并,只是把依赖 jar 移动到最终 jar 包的 lib 目录下,然后在 manifest 中把 class-path 指向这个地址,那么加载类时就能正确找到
  3. 嵌套 jars, SpringBoot Gradle plugin 在用,需要用它的启动器自定义 ClassLoader 来启动,会加入很多业务无关代码

优点

  • 结构和原理简单
  • 不会有无关代码

缺点

  • 类文件路径被改了, 因此如果有直接调用 Class 文件名字或路径进行类加载的代码,会报错

1.4 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_GROUPshadow.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.manifestshadow.doFirst {def files = project.configurations.findByName(ShadowBasePlugin.CONFIGURATION_NAME).filesif (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<Project>() {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.shadowMavenPom pom = upload.repositories.mavenDeployer.pompom.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

2. 上传源码到 Maven

按照第一节的内容,已经完成了打包 fatJar 并上传 Maven 的操作,但我发现使用 uploadShadow 这个 Shadow 插件自带的 task 上传的 jar 包里,不带源码!不爽,看看能不能改进。

2.1 不使用 uploadShadow 任务,但调用 shadowJar 任务做打包

实现方案如上述标题,默认的 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.versionartifactId project.namegroupId project.grouppackaging 'jar'description project.description}}
}uploadArchives {configuration = configurations.archivesrepositories {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.versionartifactId project.namegroupId project.grouppackaging '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)

3. 相同需求用Maven怎么做

Maven就是xml丑了点,其实配置很简单


<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.0</version><executions><execution><goals><goal>shade</goal></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.plugins</groupId><artifactId>maven-source-plugin</artifactId><configuration><attach>true</attach></configuration><executions><execution><!-- 将goal绑定到verify这个节点,可以避免每次compile代码时候都做打包源码 --><phase>verify</phase><goals><goal>jar</goal></goals></execution></executions></plugin></plugins></build>

替换${app.main.class}为你的main函数所在的类
${maven.compile.source}是你的项目源码的JDK版本如1.8
${maven.compile.target}是你的项目编译的Java版本如1.8

这篇关于Gradle 打包依赖为 fatJar 添加源码上传到 Maven的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/731432

相关文章

每天认识几个maven依赖(ActiveMQ+activemq-jaxb+activesoap+activespace+adarwin)

八、ActiveMQ 1、是什么? ActiveMQ 是一个开源的消息中间件(Message Broker),由 Apache 软件基金会开发和维护。它实现了 Java 消息服务(Java Message Service, JMS)规范,并支持多种消息传递协议,包括 AMQP、MQTT 和 OpenWire 等。 2、有什么用? 可靠性:ActiveMQ 提供了消息持久性和事务支持,确保消

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

30常用 Maven 命令

Maven 是一个强大的项目管理和构建工具,它广泛用于 Java 项目的依赖管理、构建流程和插件集成。Maven 的命令行工具提供了大量的命令来帮助开发人员管理项目的生命周期、依赖和插件。以下是 常用 Maven 命令的使用场景及其详细解释。 1. mvn clean 使用场景:清理项目的生成目录,通常用于删除项目中自动生成的文件(如 target/ 目录)。共性规律:清理操作

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip