使用 `publishToMavenLocal` 构建可以发布到 maven 本地 repo 的可执行 jar - spring boot 项目

Posted

技术标签:

【中文标题】使用 `publishToMavenLocal` 构建可以发布到 maven 本地 repo 的可执行 jar - spring boot 项目【英文标题】:Building an executable jar that can be published to maven local repo with `publishToMavenLocal` - spring boot project 【发布时间】:2020-08-09 10:07:18 【问题描述】:

在构建可执行 jar(可以使用 java -jar app.jar 运行)方面需要帮助,该 jar 可以发布到 Spring Boot 项目的 maven 本地存储库(使用 gradle publishToMavenLocal)。

使用这些 gradle 设置 gradle publishToMavenLocal 有效。

Gradle 版本是 6.3 Spring Boot 版本为 2.2.6 Kotlin 1.3.71

父母build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

buildscript 
    repositories 
        mavenLocal()
        mavenCentral()
    
    dependencies 
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.6.RELEASE")
    


plugins 
    id("org.springframework.boot") version "2.2.6.RELEASE" apply false
    id("io.spring.dependency-management") version "1.0.9.RELEASE" apply false
    kotlin("jvm") version "1.3.71" apply false
    kotlin("plugin.spring") version "1.3.71" apply false
    `maven-publish`
    `java-library`
    signing


allprojects 
    group = "com.example.user"
    version = "0.0.1-SNAPSHOT"

    tasks.withType<JavaCompile> 
        sourceCompatibility = "1.8"
        targetCompatibility = "1.8"
    

    tasks.withType<KotlinCompile> 
        kotlinOptions 
            freeCompilerArgs = listOf("-Xjsr305=strict")
            jvmTarget = "1.8"
            incremental = false
        
    


subprojects 
    repositories 
        mavenLocal()
        mavenCentral()
    

    apply 
        plugin("io.spring.dependency-management")
        plugin("maven-publish")
    

子项目build.gradle.kts:

plugins 
    id("org.springframework.boot")
    id("io.spring.dependency-management")
    kotlin("jvm")
    kotlin("plugin.spring")



val developmentOnly by configurations.creating
configurations 
    runtimeClasspath 
        extendsFrom(developmentOnly)
    


dependencies 
    implementation(project(":common"))
    implementation(project(":user-models"))
    implementation(project(":user-core"))
    implementation("org.springframework.boot:spring-boot-starter-webflux") 
        exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
    

    implementation("org.springframework.boot:spring-boot-maven-plugin:2.2.6.RELEASE")


    implementation(group = "org.springframework.boot", name = "spring-boot-autoconfigure", version = "2.2.6.RELEASE")
    implementation(group = "org.springframework.boot", name = "spring-boot-starter-parent", version = "2.2.6.RELEASE")

    implementation("org.springframework.boot:spring-boot-configuration-processor:2.2.6.RELEASE")

    implementation("org.projectreactor:reactor-spring:1.0.1.RELEASE")
    implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")


    implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.3.5")
    implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-reactor", version = "1.3.5")
    implementation(group = "org.springframework.boot", name = "spring-boot-starter-log4j2", version = "2.2.6.RELEASE")

    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    developmentOnly("org.springframework.boot:spring-boot-devtools")

    testImplementation("org.springframework.boot:spring-boot-starter-test") 
        exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
    



tasks.withType<Test> 
    useJUnitPlatform()


tasks.jar 
    enabled = true
    manifest 
        attributes["Main-Class"] = "com.subnub.user.service.ServiceApplicationKt"
        attributes["Class-Path"] = configurations.compileClasspath.get().map  it.name .joinToString(" ")
    

    from(configurations.compileClasspath.get().map  if(it.isDirectory) it else zipTree(it) )

    duplicatesStrategy = DuplicatesStrategy.INCLUDE



tasks.bootJar 
    enabled = false
    archiveClassifier.set("application")
    mainClassName = "com.example.user.service.ServiceApplicationKt"



publishing 
    publications 
        create<MavenPublication>("mavenJava") 
            from(components["java"])

            afterEvaluate 
                artifactId = tasks.jar.get().archiveBaseName.get()
            
        
    

但是使用这些 gradle 设置构建的 jar 在尝试运行时会中断

标准错误:

➜  example-user git:(master) ✗     java -jar example-service/build/libs/example-service-0.0.1-SNAPSHOT.jar
[INFO ] 2020-04-25 22:18:11.871 [main] ServiceApplicationKt - Starting ServiceApplicationKt on admins-mbp-7 with PID 4663 (/Users/zerocase/personal/startups/example/code/example-user/example-service/build/libs/example-service-0.0.1-SNAPSHOT.jar started by abhishek in /Users/zerocase/personal/startups/example/code/example-user)
[INFO ] 2020-04-25 22:18:11.874 [main] ServiceApplicationKt - No active profile set, falling back to default profiles: default
[ERROR] 2020-04-25 22:18:12.055 [main] SpringApplication - Application run failed
java.lang.IllegalArgumentException: No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.
        at org.springframework.util.Assert.notEmpty(Assert.java:464) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getCandidateConfigurations(AutoConfigurationImportSelector.java:173) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:116) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:396) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:878) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:808) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:779) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:192) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:319) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:706) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66) ~[example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) [example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [example-service-0.0.1-SNAPSHOT.jar:?]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [example-service-0.0.1-SNAPSHOT.jar:?]
        at com.example.user.service.ServiceApplicationKt.main(ServiceApplication.kt:19) [example-service-0.0.1-SNAPSHOT.jar:?]

我已经尝试了此处建议的解决方法 (https://docs.gradle.org/current/userguide/upgrading_version_6.html#publishing_spring_boot_applications) 来发布 bootJar 工件。在这种情况下,jar 可以正常工作,但是,

configurations 
    listOf(apiElements, runtimeElements).forEach 
        it.get().outgoing.artifacts.removeIf  it.buildDependencies.getDependencies(null).contains(tasks.jar) 
        it.get().outgoing.artifact(tasks.bootJar)
    

但是 gradle 不允许我这样做 gradle pTML

➜  example-user git:(master) ✗ gradle pTML
> Task :user-service:publishMavenJavaPublicationToMavenLocal FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':user-service:publishMavenJavaPublicationToMavenLocal'.
> Failed to publish publication 'mavenJava' to repository 'mavenLocal'
   > Artifact example-service-0.0.1-SNAPSHOT.jar wasn't produced by this build.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s
16 actionable tasks: 3 executed, 13 up-to-date

【问题讨论】:

【参考方案1】:

解决了问题,artifact 需要在发布范围内定义。

publishing 
    publications 
        create<MavenPublication>("mavenJava") 
            //from(components["java"]) // <-- this is not required
            artifact(tasks.bootJar.get()) // <-- this is required
        
    

并对configurations 范围(ref)稍作修改:

configurations 
    listOf(apiElements, runtimeElements).forEach 
        // Method #1
        val jar by tasks
        it.get().outgoing.artifacts.removeIf  it.buildDependencies.getDependencies(null).contains(jar) 

        // Method #2
        it.get().outgoing.artifact(tasks.bootJar)
    

这里是一些发布出版物的示例implementations。

【讨论】:

【参考方案2】:

我修复了错误Artifact ....jar wasn't produced by this build 添加参数enabled = truejar 任务

jar 
   enabled = true

【讨论】:

以上是关于使用 `publishToMavenLocal` 构建可以发布到 maven 本地 repo 的可执行 jar - spring boot 项目的主要内容,如果未能解决你的问题,请参考以下文章

测试使用

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”