maven - 用于接口和Spring实现的独立模块

时间:2013-02-13 14:50:35

标签: java spring maven

我们正在研究Mavenizing我们的java项目,我们希望在每个模块的接口和实现之间建立一个清晰的分离。 为了做到这一点,我们希望将每个模块分成两个子模块,一个用于它们使用的接口和数据对象,另一个用于实现。 例如:

 +commons 
  +commons-api 
  +commons-impl 

将配置模块的POM,使得没有模块依赖于impl子模块。这样,来自一个模块的代码就无法“看到”另一个模块的实现细节。

我们遇到的问题是,将Spring XML放在何处。 在我们的项目中,我们使用通配符导入(如

)自动导入spring XML文件

<import resource="classpath*:**/*-beans.xml"/>

这样,Spring XML的位置在运行时并不重要,因为所有模块都被加载到同一个类加载器中,并且POM中严格的单向依赖规则不适用。

但是,在开发过程中,我们希望IDE(我们使用Intellij IDEA)识别从spring XML引用的实现类。 我们还希望IDEA识别其他模块中定义的bean。

如果我们将spring XML放在API子模块中 - 他们将不会“看到”impl子模块中的实现类。 如果我们将它们放在impl子模块中,它们的bean将不会从其他模块中“看到”。 可能可以将IDEA项目配置为从没有依赖项的模块中识别spring XML,但我们更喜欢POM保存所有项目结构信息而不依赖于IDEA项目文件。

我们考虑创建第三个子模块只是为了保存Spring XML(也许还有hibernate xmls)。例如:

 +commons 
  +commons-api 
  +commons-impl 
  +commons-config

外部模块将取决于 commons-api commons-config commons-config 将取决于公共-api commons-impl ,依赖 commons-impl 标记为“已提供”(以防止传递解析)。

然而,这似乎是一个复杂而笨拙的解决方案,我们认为必须有一种更好 - 更简单的方法来实现与Maven和Spring的接口/ impl分离。

6 个答案:

答案 0 :(得分:7)

您需要的是运行时依赖范围:

  

运行时 - 此作用域表示编译不需要依赖项,但是用于执行。它位于运行时和测试类路径中,但不是编译类路径。

https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

定义从一个impl模块到另一个impl模块的运行时依赖项,您可以在其中使用* -beans.xml配置中的impl类。 Intellij会在spring配置文件中正确识别它,但不会在代码中自动完成它们(但它会在测试代码中完成)。

此外,如果有人在代码中使用了这些类,那么通过maven进行编译会失败,因为运行时依赖项不在编译类路径上。

答案 1 :(得分:2)

你可以像这样实现api和impl的解耦:

+ commons (pom)
  + pom.xml         <--- serves as a parent aggregator (see below)
  + commons-api (jar)  <--- contains models, interfaces and abstract classes only
  + commons-impl (jar)  <--- depends on commons-api
  + commons-config (jar) <--- depends on commons-impl only (no need to depend on commons-api as it is brought in transitively)

 + external-project (war or jar) <--- has commons-config as a dependency

父聚合器pom(指定构建顺序):

<modules>
  <module>commons-api</module>
  <module>commons-impl</module>
  <module>commons-config</module>
</modules>

如果配置模块仅包含spring应用程序上下文配置,则可以省略它。应用程序配置xml应位于包含要部署的工件的模块的类路径和文件夹结构中。因此,如果您正在构建战争工件,应用程序上下文应该在那里。

应该在commons模块中的唯一配置是在impl模块的测试包中。

答案 2 :(得分:1)

简而言之,您希望Idea覆盖maven依赖关系图,但避免将此配置保存在想法项目文件中?

一种选择是将实现依赖关系分组到maven配置文件中。默认情况下不会启用此配置文件,但您应该能够在想法下将其标记为活动状态。

答案 3 :(得分:1)

我想到了两个想法:

  1. 您将拥有一个(或多个)模块,其中所有模块(api + impl)都是依赖项,您可以将弹簧配置文件放在那里。
  2. 将spring配置文件放在api模块中,并使用范围provided声明对impl模块的依赖关系,这样实现将是已知的,而api对部署没有依赖性。

答案 4 :(得分:1)

  • commons-impl在外部模块的运行时范围

    1. commons(pom dependencyManagement)=&gt;

      + commons-api(编译)

      + commons-impl(compile)

      + commons-config(编译)

    2. commons-impl(pom dependencies)=&gt;

      + commons-api(编译)

      + commons-config(编译)

    3. 外部模块(pom依赖项)=&gt;

      + commons-impl(运行时

      + commons-api(编译)

      + commons-config(编译)

答案 5 :(得分:1)

  1. 尽可能少地保留模块编号;

    这加快了项目构建时间并简化了布局。

  2. 使模块结构尽可能简单:单根+同一文件夹中的所有子模块,例如: G:

    pom.xml
    commons-api/
    commons-runtime/
    module-a-api/
    module-a-runtime/
    ... 
    
  3. 当模块编号非常高(> 50)

    时,这简化了项目的导航
    1. 仅在需要时才为运行时模块提供运行时范围的依赖项;

      这可以保持您的架构清晰。使用mocks而不是显式依赖于另一个运行时模块。

    2. 在api模块中保留api spring上下文,将您的公共bean定义为abstract bean + interface;

    3. 将您的实现上下文保留在运行时模块中,通过spring配置文件覆盖api bean(使用&lt; beans profile =&#34;默认&#34;)。

    4. 结果:简单,透明的布局和设计;全力支持;没有对运行时模块内部的明确依赖。