【问题标题】:How to resolve circular dependency in Gradle如何解决 Gradle 中的循环依赖
【发布时间】:2016-10-29 23:48:17
【问题描述】:

我正在将一个 Java 项目从 Ant 迁移到 Gradle。我认为最好的解决方案是使用 Gradle 的多项目支持,但我找不到摆脱循环依赖的方法。

原始项目设置为具有此布局:

- project/
  - common/
  - product-a/
  - product-b/

commonproduct-aproduct-b 之间的关系很棘手。 common 取决于 product-aproduct-b,具体取决于配置文件。同样,product-aproduct-b 依赖于 common,无论配置属性如何。 product-aproduct-b 永远不会同时构建。

我认为一个快速的解决方案是在 project/build.gradle 中使用类似的东西:

project(':product-a') {
    dependencies {
        compile project(':common')
    }
}

project(':product-b') {
    dependencies {
        compile project(':common')
    }
}

接下来,我想找到一种方法让这更接近于为 product-a 工作。这导致我这样做:

project(':common') {
    dependencies {
        compile project(':product-a')
    }
}

这将引发循环依赖异常。

我考虑通过设置commonproduct-a/product-b 预期的类的接口或使用多态性来重构product-aproduct-b,但在我继续使用其中任何一个之前,有没有更好的方法来用 Gradle 完成这个?我还没有准备好摆脱这种技术债务。

【问题讨论】:

    标签: java gradle


    【解决方案1】:

    使用构建技巧无法解决删除循环依赖项的问题。您将不得不重构您的模块,以便不再存在循环依赖。从您的模块名称中,并且没有其他信息,我认为您希望提取依赖于“product-*”的“common”部分并将其放入新模块中。

    【讨论】:

    【解决方案2】:
    project(':project-a') {
        dependencies {
            compile project(':project-b')
        }
    }
    
    project(':project-b') {
        dependencies {
            //circular dependency to :project-a
            compile project(':project-a')
        }
    
    
       compileJava {
           doLast {
               // NOTE: project-a needs :project-b classes to be included
               // in :project-a jar file hence the copy, mostly done if we need to  
               // to support different version of the same library
               // compile each version on a separate project
              copy {
                   from "$buildDir/classes/java/main"
                   include '**/*.class'
                   into project(':project-a').file('build/classes/java/main')
    
         }
       }
    
     }
    }
    

    product-a --> build.gradle

    /**
     * Do nothing during configuration stage by
     * registering a GradleBuild task
     * will be referenced in the task compileJava doLast{}
     */
    tasks.register("copyProjectBClasses", GradleBuild) {
      //we'll invoke this later
      def taskList = new ArrayList<String>()
      taskList.add(":product-b:compileJava")
      logger.lifecycle "Task to execute $taskList..."
      setTasks(taskList)
    }
    
    // make sure :project-b classes are compiled first and copied to this project before 
    // all classes are added to the jar, so we do it after :project-a compiled.
    compileJava {
      doLast {
        synchronized(this) {
          // create temp file to avoid circular dependency
          def newFile = new File("$buildDir/ongoingcopy.tmp")
          if (!newFile.exists()) {
            newFile.createNewFile()
            GradleBuild buildCopyProjectBClasses = tasks.getByName("copyProjectBClasses")
            buildCopyProjectBClasses.build()
          }
          newFile.delete()
        }
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-01-30
      • 2012-03-15
      • 2016-09-21
      • 2020-11-12
      相关资源
      最近更新 更多