• 【效率提升】maven 转 gradle 实战 | 京东云技术团队


    一、灵魂三问

    1、gradle 是什么?

    一个打包工具, 是一个开源构建自动化工具,足够灵活,可以构建几乎任何类型的软件,高性能、可扩展、能洞察等。其中洞察,可以用于分析构建过程中数据,提供分析参考,方便排查问题和不断优化构建性能,以下一次编译分析报告。

    2、有什么优势

    参考官方文章,针对包含10 子模块的工程,相对 maven 构建速度,大概有 2-3 倍的性能提升,增量编译大概 7 倍的性能提升,参考官方

    实测对比:

    ****gradle 耗时maven 耗时
    全新构建(clean 及下载依赖包)1m 35s1m58s
    全新构建(clean)43s60s
    增量构建14s43s

    gradle 执行命令: time gradle clean build package -x test

    mvn 执行的命令: time mvn clean package -Dmaven.test.skip=true -f $(pwd) -T 1C -Dmaven.artifact.threads=16

    综述,经过多轮测试,在增量编译场景优势比较突出平均有 2 倍的性能提升,工程模块越多效率提升越大。

    3、迁移是否容易

    摸着心口说,并不容易,虽然官方提供了一键迁移的工具,但是还是有一定学习成本,但改造完成确实节省了大把的时间,尤其是改了一两行代码再次编译时。

    二、动动手试试

    1、安装 gradle

    推荐使用 sdkman ,主要用于工具多版本管理的工具,如 java 、gradle 、maven 等可以根据实际情况安装使用其中某个一个版本,如jdk8,jdk11 等,版本间切换非常简便。 sdk 介绍

    sdk install  gradle 8.1.1
    
    • 1

    2、执行迁移命令

    在当前 maven 工程下,执行如下的命令。

    gradle init 
    Found a Maven build. Generate a Gradle build from this? (default: yes) [yes, no] yes
    Select build script DSL:
      1: Groovy
      2: Kotlin
    Enter selection (default: Groovy) [1..2] 1
    Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] no
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    不出意外下,会在默认子模块下添加 build.gradle 文件,如下图:

    文件解释:

    1)buildSrc/main/groovy/com.jd.pegasus.java-conventions.gradle :里面配置的是内网私服库地址。

    repositories {
        mavenLocal()
        maven {
            url = uri('http://artifactory.jd.com/libs-releases')
    
            allowInsecureProtocol = true
        }
    
        maven {
            url = uri('http://artifactory.jd.com/libs-snapshots')
    
             allowInsecureProtocol = true
        }
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2)gradle.properties :配置环境变量,必须设置 jvm 的参数,否则很容易 oom 。 更多配置

    # gradle jvm 设置
    org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
    # 开启并行编译
    org.gradle.parallel=true
    
    • 1
    • 2
    • 3
    • 4

    3)build.gradle :包含了编译过程中使用的插件,id ‘com.jd.pegasus.java-conventions’ 代表自定义的插件。 dependencies 为工程所使用的依赖。

    plugins {
        id 'com.jd.pegasus.java-conventions'
    }
    
    dependencies {
        api project(':pegasus-service')
        api project(':pegasus-common')
        implementation 'org.springframework.boot:spring-boot:2.1.9.RELEASE'
        api project(':component-metric')
        testImplementation 'org.springframework.boot:spring-boot-starter-test:2.1.9.RELEASE'
        annotationProcessor 'org.projectlombok:lombok:1.18.10'
    }
    
    description = 'pegasus-worker'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里面有一个dependencies 依赖项中 api 与 implementation 区别,参见如下解释:

    假设你正在维护一个名为 MyLibrary 的库,它依赖于另一个库 InternalLibrary。你希望 MyLibrary 的用户能够使用 InternalLibrary 中的某些类和方法,但不希望他们使用其他类和方法。在这种情况下,你可以在 MyLibrary 的 build.gradle 文件中使用 api 配置来声明对 InternalLibrary 的依赖: dependencies { api project(‘:InternalLibrary’) } 这样,当其他模块依赖于 MyLibrary 时,它们也能够访问 InternalLibrary 中的类和方法。 但是,如果你不希望 MyLibrary 的用户能够访问 InternalLibrary 中的任何内容,你可以在 MyLibrary 的 build.gradle 文件中使用 implementation 配置来声明对 InternalLibrary 的依赖: dependencies { implementation project(‘:InternalLibrary’) } 这样,当其他模块依赖于 MyLibrary 时,它们将无法访问 InternalLibrary 中的任何内容。

    简单点就是如果你想把你依赖组件,让使用你组件人也知道的明明白白的也能使用,那你就用 api 把组件传递下去 ,反之就用 implementation ,就自个偷摸使用了,对第三方隐藏了一些内部细节。

    3、gitignore 排除不要的目录和文件

    # Gradle generated files
    build/
    .gradle/
    /out/
    /.gradle/
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4、允许以不安全的方式访问私服库

    # 在这个文件里面,buildSrc/main/groovy/com.jd.pegasus.java-conventions.gradle 
    
    repositories {
        mavenLocal()
        maven {
            url = uri('http://artifactory.jd.com/libs-releases')
    
            allowInsecureProtocol = true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5、解决 lombok 引发的编译问题

    通过 lombok 注解会在编译过程中把注解的类进行扩展,添加 get 、set 、toString 方法等。

    # 在编译出错的模块里面 build.gradle 文件中添加注解处理器,annotationProcessor  如下:
    dependencies {
        api project(':pegasus-service')
        annotationProcessor 'org.projectlombok:lombok:1.18.10'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    6、解决版本依赖冲突

    版本冲突指同依赖组件出现不同的版本情况,如pegasus-common 模块依赖的 fastjson 有1.2.83-jdsec.rc1, 1.2.29 and 1.2.12 三个版本,gradle 会自动处理仲裁,规则有以下几点:

    1)冲突时会默认采用最新的版本。

    2)通过 strictly 标记主要用于降级到指定的版本,如传递依赖引入的版本高,当前版本不兼容,那可以通过这个关键字设置指定的版本。

    implementation('com.alibaba:fastjson'){   
      version{       
        strictly("1.2.12")    
      } 
    }
    或者简写为 
    implementation 'com.alibaba:fastjson:1.2.29!!!'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3)force 的优先级会比较高,会覆盖 strictly 策略

    configurations.all {
        resolutionStrategy {
            // 在这里定义您的依赖解析规则
            //force 'com.alibaba:fastjson:1.2.12'
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    排查某个模块的依赖冲突

    gradle :pegasus-common:dependencyInsight --configuration compileClasspath --dependency com.alibaba:fastjson
    
    • 1

    7、如何构建 zip 包

    以 springboot 为例,参考如下代码即可,在子工程 build.gradle 文件里。

    。
    plugins {
        id 'com.jd.pegasus.java-conventions'
        // 引入springboot 插件
        id 'org.springframework.boot' version '2.5.6'
    }
    
    // 指定 jar 启动的入口函数
    bootJar {
        manifest {
            attributes 'Main-Class': 'com.jd.pegasus.Application'
        }
    }
    // 构建 zip 压缩包,包含启动脚本 bin 目录和 配置文件 conf 目录
    task packageZip(type: Zip) {
        archiveFileName = "${project.name}-${project.version}.zip"
        destinationDirectory = file("${project.buildDir}")
    
        from("${project.projectDir}/src/main/bin") {
            into "bin"
        }
        from("${project.buildDir}/resources/main/conf") {
            into "conf"
        }
    
        from("${project.buildDir}/libs/${project.name}-${project.version}.jar") {
            into "lib"
        }
        // 表示此任务的运行依赖其它 子任务。
        dependsOn bootJar
        dependsOn build
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    8、执行构建命令

    # -x test 排除单测
    gradle clean  build package -x test  
    
    • 1
    • 2

    三、附录参考

    1.一文搞懂Gradle的依赖管理和版本决议

    1. gradle 与 maven 性能对比

    2. 爬坑指南 – 理解 Plugin、Task、构建流程

    3. 如何定位和解决依赖冲突

    4. Gradle依赖之‘五种依赖配置’

    5. Migrating Builds From Apache Maven

    后记:

    听说 maven 不甘寂寞,由 gradle 和 Takari 的灵感,做了一个守护的 mvnd ,在增量编译场景效率杠杠的,有时间测试对比下。 mvnd 参考

    作者:京东科技 宁利广

    来源:京东云开发者社区 转载请注明来源

  • 相关阅读:
    websocket
    Blazor预研与实战
    苹果手机用什么无线耳机比较好?苹果耳机平替品牌推荐
    Redis内存回收
    OpenAI 更新 ChatGPT:支持图片和语音输入【附点评】
    数据采集与大数据架构分享
    【数据结构】双向链表
    【Vue.js】条件渲染和列表渲染以及Vue中key值的内部原理
    南大通用数据库-Gbase-8a-学习-05-通过审计日志抓取Sql、Trace日志查看执行计划
    pymysql连接Mariadb/Mysql出现错误(配置正确情况下)解决办法
  • 原文地址:https://blog.csdn.net/JDDTechTalk/article/details/133171257