【问题标题】:Property 'mainClass' is final and cannot be changed any further属性“mainClass”是最终的,不能进一步更改
【发布时间】:2020-09-12 16:46:41
【问题描述】:

我最近为一个 Spring Boot 项目将 Gradle 从 4.x 升级到了 6.6。我以为我终于把它全部启动并运行了,但后来意识到我们的一个应用程序可能会启动两个不同配置的 BootRun 类型的任务(比如 A 和 B),但无法启动第二个 B 实例。

这是我尝试运行第二个实例时的错误:

Build file 'C:\Users\...\build.gradle' line: 17

Execution failed for task ':apps:myapp:bootRunB'.
> The value for task ':apps:myapp:bootRunB' property 'mainClass' is final and cannot be changed any further.

这是我的build.gradle 文件中用于配置任务的部分:

task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
    group = 'Application'

    doFirst() {
        main = bootJar.mainClassName
        classpath = bootRun.classpath
        systemProperty '...'
    }
}

任何建议将不胜感激。

【问题讨论】:

  • 参见github.com/gradle/gradle/issues/12841 :将main 属性配置移到doFirst 块之外,在执行阶段无法更改此属性
  • @M.Ricciuti 这可能会导致在配置bootRunB 后更改bootJar.mainClassName 时出现问题。我想这些问题是首先使用doFirst 的原因。

标签: gradle build.gradle


【解决方案1】:

Gradle 最近为 lazy configuration 引入了一个 API,现在插件扩展和任务类型等内部功能已迁移到这个新 API。总之,可以说(几乎)构建脚本中的每个配置属性都应该使用Property<T>Provider<T>(只读)而不是简单类型T 的getter 和setter 来实现。为了提供向后兼容性,很多属性没有改变,但添加了新属性,旧属性读取和写入这些新属性。

这也是您的问题的情况,因为 Spring Boot Gradle 插件中的任务类型 BootRun 扩展了 Gradle 提供的 JavaExec 任务类型。添加了新属性 mainClass (a Property<string>) 并修改了旧属性 main 以使用新属性。方法 getMain() 使用 Property.get()mainClass 读取,而在 Groovy 中使用语法 main = '...' 时调用的方法 setMain(String) 使用 Property.set(String) 写入 mainClass

这一切都可以正常工作,但 Gradle 为他们的惰性配置 API 引入了一些额外的功能。这些功能之一是可写属性的最终确定 (Property<>)。在构建的某个时间点,将读取任务属性(例如mainClass)以用于其原始目的(运行任务),因此该点之后的任何更改都不会生效。在早期版本的 Gradle 中,这会导致很多难以调试的问题,因为 Gradle 不会显示任何错误。现在,这些属性一旦被读取以用于其原始用途,就会最终确定,当有人随后尝试更改它们时,会导致 Gradle 失败。

关于您的用例,mainClass 属性(由main 访问)已经在doFirst 闭包中完成,因此您需要提前应用此配置。由于要将值设置为属性bootJar.mainClassName(即简单的String)的值,因此必须确保该属性具有最终值,然后才能读取它以配置bootRunB 任务:

bootJar {
    mainClassName = '...'
}

task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
    group = 'Application'

    main = bootJar.mainClassName
    // From this point, bootRun.classpath must not be changed !
    classpath = bootRun.classpath
    systemProperty '...'
}

要消除这种对配置顺序的依赖,您可以使用Project.provider(...) 创建一个Provider<String>

task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
    group = 'Application'

    main = provider({ bootJar.mainClassName })
    classpath = bootRun.classpath
    systemProperty '...'
}

【讨论】:

    【解决方案2】:

    这是我们昨天修复它的方法:

    task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
        group = 'Application'
        mainClass = 'com.App'
    
        doFirst() {
            main = bootJar.mainClassName
            classpath = bootRun.classpath
            systemProperty '...'
        }
    }
    

    【讨论】:

      【解决方案3】:

      当配置不正确时会出现此问题,因此您需要选择适当的 shorten line 命令。 看下面的截图

      注意:在我的情况下 user-local default: none 正在工作

      【讨论】:

        【解决方案4】:

        bootRun 任务是基于 JavaExec 任务的。

        它现在提供了一个接受提供者的新属性。 不用填充 main 属性,而是填充 mainClass 属性:

        kts 脚本语言示例:

        
        tasks.register<BootRun>("myBootRunTask") {
            dependsOn("assemble")
            group = "Application"
        
            val bootJarTask = tasks.getByName<BootJar>("bootJar")
        
            mainClass.set(provider { bootJarTask.mainClassName })
            classpath = bootJarTask.classpath
        
           ...
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-04-28
          • 2018-12-31
          • 2022-06-12
          • 1970-01-01
          • 1970-01-01
          • 2022-06-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多