【4】Gradle-快速入门使用【Gradle多模块项目详解】

目录

  • 【4】Gradle-快速入门使用【Gradle多模块项目详解】
    • 创建多项目构建
    • 添加子项目
      • 命名建议
    • 项目依赖项
      • 项目路径
      • 不同模块的`build.gradle`配置
    • 子项目之间共享构建逻辑
      • 公约插件
      • 跨项目配置
      • buildSrc开发公约插件
    • 调整多模块项目配置
        • 修改项目树的元素
    • 了解Gralde配置时间和执行时间
      • 并行项目执行
      • 解耦项目

个人主页: 【⭐️个人主页】
需要您的【💖 点赞+关注】支持 💯


在这里插入图片描述

【4】Gradle-快速入门使用【Gradle多模块项目详解】

📖 本文核心知识点:

  • 多模块项目
  • 多模块添加子项目
  • 多模块全局配置方式
  • 多模块间依赖
  • 多模块项目调整
  • 多模块buildSrc目录
  • 多模块配置时间和执行时间
  • 并行执行和解耦合

创建多项目构建

Gradle中的多项目构建由一个根项目一个或多个子项目组成。

基本的多项目构建包含一个根项目和一个子项目。这是一个多项目构建的结构,它包含一个名为app的子项目:

├── app
│   ...
│   └── build.gradle
└── settings.gradle

这是启动任何Gradle项目的推荐项目结构。build init插件还会生成遵循这种结构的框架项目——一个根项目和一个子项目。

🅰️ 注意:根项目没有Gradle构建文件build.gradle,只有一个定义要包含的子项目的设置文件settings.gradle
settings.gradle

rootProject.name = 'basic-multiproject'
include 'app'

在这种情况下,Gradle将在app目录中查找构建文件。

我们可以通过运行gradlew projects命令来查看多项目构建的结构
在这里插入图片描述
使用 gradle -q run命令,启动app项目

添加子项目

假设我们想要在前面创建的项目中添加另一个名为infrastructure的子项目。我们所需要做的就是在根设置文件中添加另一个include语句:
settings.gradle

rootProject.name = 'study-spring3'
include 'app'
// 基础设施模块
include 'infrastructure'

结构:

.
├── app
│   ...
│   └── build.gradle
├── infrastructure
│   ...
│   └── build.gradle
└── settings.gradle

命名建议

随着项目的发展,命名一致性变得越来越重要。为了保持构建的可维护性,我们建议采用以下方法:

  • 子项目保留默认项目名称:可以在设置文件中配置自定义项目名称。但是,对于开发人员来说,跟踪哪个项目属于哪个文件夹是不必要的额外工作。

  • 对所有项目名称使用kebab大小写格式:

    kebab大小写格式是指所有字母都是小写,单词之间用破折号(' - ')字符分隔(例如kebab-case-formatting)。这已经是许多大型项目的实际模式。此外,Gradle支持kebab case名称缩写。

  • 在设置文件settings.gradle中定义根项目名称``:' rootProject.name '有效地为整个构建分配了一个名称,这在构建扫描等报告中使用。如果没有设置根项目名称,则名称将是容器目录名称,这可能是不稳定的(即您可以将项目签出到任何目录)。如果没有设置根项目名,并且它被检出到文件系统的根目录(例如/或C:),则该名称将随机生成。

项目依赖项

❓ 如果一个项目需要另一个项目在其编译类路径上生成的jar,该怎么办?
❓ 如果它还需要其他项目的传递依赖项怎么办?
显然,这是Java多项目构建中非常常见的用例。正如在项目依赖项中提到的,Gradle为此提供了项目依赖项

├── buildSrc
│   ...
├── api
│   ├── src
│   │   └──...
│   └── build.gradle
├── domain-core
│   ├── src
│   │   └──...
│   └── build.gradle
├── infrastructure
│   ├── src
│   │   └──...
│   └── build.gradle
└── settings.gradle

有三个项目api, domain-core, infrastructure,它们之间的依赖关系是
在这里插入图片描述

我们使用:分隔符来定义项目路径

项目路径

给定的项目添加到构建中。所提供列表中的每个路径都被视为要添加到构建中的项目的路径。注意,这些路径不是文件路径,而是指定新项目在项目层次结构中的位置。因此,所提供的路径必须使用':'字符作为分隔符(而不是’/')。
所提供路径的最后一个元素用作项目名称。提供的路径被转换为相对于根项目目录的项目目录。项目目录可以在项目被包含之后通过改变’projectDir'属性来改变(参见ProjectDescriptor.setProjectDir(java.io.File))

使用项目路径的一些常见示例如下:

// include two projects, 'foo' and 'foo:bar'
// directories are inferred by replacing ':' with '/'
include 'foo:bar'// include one project whose project dir does not match the logical project path
include 'baz'
project(':baz').projectDir = file('foo/baz')// include many projects whose project dirs do not match the logical project paths
file('subprojects').eachDir { dir ->include dir.nameproject(":${dir.name}").projectDir = dir
}

不同模块的build.gradle配置

  1. app
    dependencies {implementation 'org.apache.commons:commons-text'implementation project(':infrastructure')}
    
  2. infrastructure
    	dependencies {implementation project(':domain-core')}
    
  3. domain-core 【无】

执行gradle app:dependencies查看app的依赖项
在这里插入图片描述

子项目之间共享构建逻辑

公约插件

通常,多项目构建中的子项目具有一些共同的特征

例如,几个子项目可能包含特定编程语言的代码,而另一个子项目可能专门用于文档。代码质量规则适用于所有代码子项目,但不适用于文档子项目。与此同时,具有共同特征的子项目可能服务于不同的目的——它们可能会产生不同的工件类型来进一步区分它们,
例如:
1. 公共库 : 发布到某些Mavne存储库的库
2. 内部库 :其他子项目在项目内部依赖的库
3. 命令行应用程序 : 具有特定打包要求的应用程序
4. Web服务 : 具有与上述不同的特定包装要求的应用程序
等等
其他一些代码子项目可能专门用于测试目的等。
以上特征标识了子项目的类型。或者换句话说,子项目的类型告诉我们该项目具有哪些特征

所以: 多个特征组成了一类的子项目。一类子项目可以作为公共构建进行子项目间共享。

Gradle推荐组织构建逻辑的方法使用其插件系统

  1. 插件应该定义子项目的类型

    事实上,Gradle核心插件以相同的方式建模
    例如,Java插件配置了一个通用的java项目,而Java库插件在内部应用Java插件,并配置特定于Java库的方面。同样,应用程序插件应用和配置Java插件和分发插件

  2. 您可以通过应用和配置核心和外部插件来编写自定义构建逻辑,并创建自定义插件,定义新的项目类型配置特定于您的项目或组织的约定(特征)。

跨项目配置

另一种令人沮丧的子项目之间共享构建逻辑的方法是通过
subprojects {} allprojects {} DSL结构进行跨项目配置。
通过交叉配置,构建逻辑可以注入子项目,在查看子项目的构建脚本时,这并不明显,这使得理解特定子项目的逻辑更加困难。从长远来看,交叉配置通常会随着越来越多的条件逻辑和更高的维护负担而变得复杂。

交叉配置还可以引入项目之间的配置时间耦合,这可能会阻止按需配置等优化正常工作。
配置时间耦合: 指下载插件和依赖的时间。指多项目构建时,子项目间交叉依赖,耦合在一起。

交叉配置有两种最常见的用途,都可以使用约定插件的方式更好地建替代:

  1. 将插件或其他配置应用于特定类型的子项目。

    通常,如果子项目是X类型,那么交叉配置部分就可以完成,然后配置y。这相当于将X-conventions插件直接应用于子项目。

  2. 从某种类型的子项目中提取信息。此用例可以使用传出的配置变体进行建模。

buildSrc开发公约插件

建议将约定插件源代码和测试放在项目根目录buildSrc目录中。

使用约定插件编写构建逻辑的多项目构建的另一个更复杂和现实的例子是Gradle构建工具本身的构建。

目录buildSrc被视为包含的构建。发现目录后,Gradle会自动编译和测试此代码,并将其放入构建脚本的类路径中。对于多项目构建,只能有一个buildSrc目录,该目录必须位于根项目目录中。buildSrc应优于脚本插件,因为它更容易维护、重构和测试代码

在这里插入图片描述

buildSrc使用适用于Java和Groovy项目的相同源代码约定。它还提供了对Gradle API的直接访问。其他依赖项可以在buildSrc下的专用build.gradle中声明。

可以使用gradle init 生成buildSrc目录。并进行约定插件的gradle编写

调整多模块项目配置

多项目构建总是由具有单个根的树表示树中的每个元素代表一个项目。项目有一个路径,表示项目在多项目构建树中的位置。在大多数情况下,项目路径项目在文件系统中的物理位置一致。然而,这种行为是可配置的。项目树在settings.gradle文件中创建。设置文件的位置也是根项目的位置。

在设置文件中,您可以使用include方法构建项目树。
通过修改settings.gradle文件中的include子项目应用。可以调整子项目的相关信息

include 'project1', 'project2:child', 'project3:child1'

include方法将项目路径作为参数。项目路径假定等于相对的物理文件系统路径。

例如,默认情况下,路径“services:api”映射到文件夹“services/api”(相对于项目根目录)。你只需要指定树的叶子。这意味着包含“services:hotels:api”路径将导致创建3个项目:“services”、“services:hotels”和“services:hotels:api”。

修改项目树的元素
rootProject.name = 'main'
include('project-a')
project(':project-a').projectDir = file('../my-project-a')
project(':project-a').buildFileName = 'project-a.gradle'

了解Gralde配置时间和执行时间

构建阶段描述了每个Gradle构建的阶段。让我们放大多项目构建的配置和执行阶段

这里的配置意味着评估项目的构建脚本文件,其中包括下载所有插件和构建脚本依赖项

默认情况下,所有项目的配置发生在执行任何任务之前。这意味着,当请求来自单个项目的单个任务时,首先配置多项目构建的所有项目。每个项目都需要配置的原因是Gradle项目模型的任何部分 访问和更改 的灵活性

并行项目执行

并行执行试图:

  1. 减少执行受IO绑定或以其他方式不会消耗所有可用CPU资源的多项目构建的总构建时间
  2. 为小型项目的执行提供更快的反馈,而无需等待其他项目的完成。

并行项目执行允许并行执行解耦多项目构建中的独立项目(另见解耦项目)。虽然并行执行在配置时并不严格要求解耦,但长期目标是提供一套强大的功能,这些功能将可用于完全解耦的项目。这些功能包括:

  • 按需配置。
  • 并行配置项目。
  • 为不变的项目重复使用配置。
  • 项目级最新检查。
  • 在构建依赖项目的地方使用预先建造的工件。

并行执行是如何工作的?

首先,您需要告诉Gradle使用并行模式。您可以使用--parallel命令行参数或配置构建环境(Gradle属性)。除非您提供特定数量的并行线程,否则Gradle会尝试根据可用的CPU内核选择正确的数量。每个并行工作线程在执行任务时都只拥有给定的项目。任务依赖性得到完全支持,并行工作线程将首先开始执行上游任务。请记住,在并行模式下,不能保证解耦任务的字母顺序,如在顺序执行过程中所示。换句话说,在并行模式下,任务一旦其依赖项完成,任务工作线程可以运行它们,任务将立即运行,这可能比它们在顺序构建期间开始时更早。您应该确保正确声明任务依赖项和任务输入/输出,以避免订购问题。

解耦项目

Gradle允许任何项目在配置和执行阶段访问任何其他项目。

如果两个项目不直接访问彼此的项目模型,它们就会脱钩。解耦的项目只能在声明的依赖项方面进行交互:项目依赖项和/或任务依赖项。任何其他形式的项目交互(即通过修改另一个项目对象或从另一个项目对象读取值)都会使项目耦合。在配置阶段耦合的后果是,如果使用“按需配置”选项调用gradle,构建的结果可能会在几个方面存在缺陷。执行阶段耦合的后果是,如果使用并行选项调用gradle,一个项目任务运行得太晚,无法影响并行项目构建的任务。Gradle不会试图检测耦合并警告用户,因为引入耦合的可能性太多。

项目耦合的一种非常常见的方法是使用配置注入。它可能不会立即显现出来,但使用allprojectssubprojects关键字等关键Gradle功能会自动使您的项目耦合。这是因为这些关键字用于定义项目的 build.gradle文件中。通常,这是一个“根项目”,只不过定义了通用配置,但就Gradle而言,这个根项目仍然是一个成熟的项目,通过使用allprojects,该项目有效地与所有其他项目耦合。将根项目耦合到子项目不会影响按需配置,但在任何子项目的build.gradle文件中使用allprojects和subprojects都会产生影响

为了充分利用跨项目配置,而不会出现并行和“按需配置”选项的问题,请遵循以下建议:

  • 避免子项目的构建脚本引用其他子项目;更喜欢从根项目进行交叉配置。
  • 避免在执行时更改其他项目的配置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/139639.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#开发的OpenRA游戏之游戏设计思路

OpenRA设计思路 在OpenRA有很多部分的内容,比如UI、渲染、单元行为等等。 不过在地图里,所有单元、建筑物、其它物品都是采用Actor来实现。每个Actor包含一系列Trait属性。 Trait属性有info类和同名称操作类构成。有一个信息类实例在同一类型的所有参与者之间共享。每个Act…

【CSS】全局声明引入自定义字体

以下用vue项目为例,其他的也是类似! 在Vue.js中可以使用全局样式表来定义字体。通常,可以在项目中的主样式表中定义全局字体,然后确保该样式表在整个应用程序中被引入。 以下是一般的步骤: 在项目中创建一个全局样式…

CDN是如何减去源机压力的

CDN也叫内容分发网络(Content Delivery Network)。分布在不同地区的节点服务器组成的分布式网络。通过中心平台的各种功能模块,可以使用户直接访问到就近的节点上,更快获取到需要的内容,大大降低了网络拥堵&#xff0c…

红黑树,AVLTree树(平衡二叉树)迭代器原理讲解

红黑树,AVLTree树底层实现逻辑都是平衡二叉树(AVLTree高度平衡,红黑树以某种规则平衡),但终究不像链表的迭代器那样逻辑简单。 简单叙述以下,二叉树上面迭代器的运行逻辑,根据下面的图&#xff…

Nginx:如何实现一个域名访问多个项目

1. 背景介绍 最近在多个项目部署中遇到这样一个问题,一个域名如何实现多个项目的访问。因为不想自己单独去申请域名证书和域名配置,便想到了这个方案,结合Nginx的location功能实现了自己的需求,便记录下来。示例中是以项目演示&a…

从TCP到Socket,彻底理解网络编程是怎么回事

进行程序开发的同学,无论Web前端开发、Web后端开发,还是搜索引擎和大数据,几乎所有的开发领域都会涉及到网络编程。比如我们进行Web服务端开发,除了Web协议本身依赖网络外,通常还需要连接数据库,而数据库连…

linux的文件属性

在使用长格式查看目录信息时,会看到如下的结果。每一行代表对应的文件或者目录的详细信息。从左到右具体含义时文件属性、文件数、所有者、所属的组、文件大小、建立月份、建立日期、建立年份或时间及文件名 [rootmaster lib]# ll total 19260 drwxr-xr-x. 2 root…

OpenWRT配置SFTP远程文件传输,让数据分享更安全

文章目录 前言 1. openssh-sftp-server 安装2. 安装cpolar工具3.配置SFTP远程访问4.固定远程连接地址 前言 本次教程我们将在OpenWRT上安装SFTP服务,并结合cpolar内网穿透,创建安全隧道映射22端口,实现在公网环境下远程OpenWRT SFTP&#xf…

Python之函数进阶-函数执行原理

Python之函数进阶-函数执行原理 函数执行流程 C语言中,函数的活动和栈有关。栈是后进先出的数据结构。栈是由底端向顶端生长,栈顶加入数据成为压栈、入栈、栈顶弹出数据称为出栈。 def add(x, y):r x yprint(r)return rdef main():a 1r add(a, 2)r…

进制的转换

1、进制的转化 (1)进制介绍 对于进制,有四种表示方法: 1)二进制:0,1,满2进1,C语言中没有二进制常数的表示方法 2)八进制:0-7,满8进1 3&#xff0…

ubuntu上如何移植thttpd

thttpd的特点 thttpd 是一个简单、小巧、便携、快速且安全的 HTTP 服务器。 简单: 它只处理实现 HTTP/1.1 所需的最低限度。好吧,也许比最低限度多一点。 小: 请参阅比较图表。它还具有非常小的运行时大小,因为它不会分叉并且非…

MapReduce编程——矩阵乘法(Python版本)

数据格式 对于矩阵元素 A i j A_{ij} Aij​&#xff0c;将其处理为 < i , j , M a t r i x N a m e , v a l u e > <i,j,MatrixName,value> <i,j,MatrixName,value>的四元组格式&#xff0c;例如矩阵[[2, 1, 3, 4], [10, -8, 7, 2], [9, 1, 6, -2]]可被转化…

牛客网刷题笔记131111 Python实现LRU+二叉树先中后序打印+SQL并列排序

从学校步入职场一年多&#xff0c;已经很久没刷过题了&#xff0c;为后续稍微做些提前的准备&#xff0c;还是重新开始刷刷题。 从未做过计划表&#xff0c;这回倒是做了个计划表&#xff0c;希望能坚持吧。 刷题比较随性且量级不大&#xff0c;今天就写了2个算法2个sql&#x…

LeetCode257. Binary Tree Paths

文章目录 一、题目二、题解 一、题目 Given the root of a binary tree, return all root-to-leaf paths in any order. A leaf is a node with no children. Example 1: Input: root [1,2,3,null,5] Output: [“1->2->5”,“1->3”] Example 2: Input: root […

uniapp本地存储的几种方式

在UniApp中&#xff0c;你可以使用本地存储来保存和获取数据&#xff0c;以便在应用的不同页面之间共享数据或在应用关闭后仍然保存数据。UniApp提供了两种主要的本地存储方式&#xff1a;uni.setStorage 和 uni.getStorage&#xff0c;以及 uni.removeStorage 用于删除数据。这…

无需公网IP,贝锐花生壳内网穿透远程访问NAS

群晖DSM 7.0及以上版本 1.1 安装运行花生壳套件 &#xff08;1&#xff09;通过浏览器输入群晖NAS的内网地址&#xff0c;登录进去后&#xff0c;点击【套件中心】&#xff0c;搜索【花生壳】&#xff0c;并点击【安装套件】&#xff1b; &#xff08;2&#xff09; 勾选我接…

ZooKeeper基本知识

1.什么是ZooKeeper ZooKeeper是一个开源的分布式协调服务&#xff0c;它提供了一个高性能、高可靠的分布式协调基础&#xff0c;用于构建分布式系统。 具体来说&#xff0c;ZooKeeper通常用于以下几个方面&#xff1a; 配置管理&#xff1a;分布式系统通常需要集中管理配置信…

Java设计模式-创建者模式-单例模式

单例模式 单例模式饿汉式懒汉式 单例模式 解释&#xff1a;一个类只能有一个实例 单例模式可以分为两种 饿汉式 和 懒汉式 饿汉式 也被称为预加载&#xff0c;即 在加载类的时候&#xff0c;就将实例创建出来&#xff0c;加载到内存&#xff0c;不管之后会不会使用这个实例 …

Jvm虚拟机

问题&#xff1a; 计算机能识别的语言是二进制&#xff0c;Java文件是程序员编写的&#xff0c;如何能够在计算机上运行&#xff1f; 以及Java为什么可以实现跨平台&#xff1f; 一Java的jdk中有jvm虚拟机 可以将文件转换为字节码文件 使得它可以在各种平台上运行&#xff0c;这…

Openlayers:自定义Controls

Openlayers是一款优秀的二维开源地图框架,支持矢量/栅格图层,支持移动端,并且易于自定义和拓展。下面来讲述一下自定义Control的基本思路。 openlayers-features 问题描述 最近在做个人项目时,遇到了一个小问题,就是在地图中心添加一个十字针形状的符号,用来表示地图中心…