如何在 Gradle 中解析循环依赖关系

2022-09-02 21:50:00

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

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

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

和 之间的关系很棘手。取决于 或 ,具体取决于配置文件。同样,无论配置属性如何,都依赖于 。 并且永远不会同时建造。commonproduct-aproduct-bcommonproduct-aproduct-bproduct-aproduct-bcommonproduct-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')
    }
}

这将引发具有循环依赖项的异常。

我已经考虑过重构,并通过设置和/或使用多态性所期望的类的接口,但是在我继续使用其中任何一个之前,是否有更好的方法来使用Gradle实现这一目标?我还没有准备好摆脱这种技术债务。product-aproduct-bcommonproduct-aproduct-b


答案 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()
    }
  }
}

推荐