近期在做一个普通javaweb项目转转换成maven项目的任务。

原项目类型:javaWeb项目

两个源码包一个产品基础包,一个基于产品基础包的开发包,两个都是普通javaWeb项目。本来应该是开发包可以单边引用产品基础包的,由于开发不规范最终导致产品基础包和开发包存在了相互引用。

针对当时我们的项目我考虑了两种方案:

方案一、将产品基础包和开发包整合成一个源码包,再重构为Maven项目。

产品基础包编译成jar包,jsp页面整合到开发包中。后续产品基础包如果升级,重新编译成jar包更换现有jar即可,涉及用到的jsp需要项目组开发团队整合到正在使用的开发包中按正常版本发布流程提交源码即可。(这种方案需人工整合为一个源码包,相互间的引用自然就不存在了)

方案二、使用Maven聚合工程

使用Maven聚合工程,将产品基础包和开发包分别构建成两个Maven模块,然后将开发包目前最新版本打成jar包放入Maven仓库由产品基础包引用从而断掉产品基础包对开发包之前的引用,之后开发时务必做到开发包单边引用产品基础包,便不会再出现产品基础包对开发包的引用。剩下的就由开发包单边依赖产品基础包即可,从而可以避免循环依赖。后续产品基础包如果升级,按正常版本发布流程提交源码即可,无需人工整合,各自提交到各自的Maven模块,无影响。(这种方案后期要确保:开发人员严格遵守开发规范开发包单边引用产品基础包)

考虑到一下两种原因,最终采用了第二种方案:

1、整合两个源码包需要花费很长时间影响进度,且整合过程中也存在一定的风险。

2、后续产品基础包可能存在升级

3、还有其他模块功能待整合上线,需要考虑到可扩展性。

 

 

后面再网上查问题时碰到更好的方案,以下内容转自:https://www.iteye.com/blog/hck-1728329l

很多时候随着项目的膨胀,模块会越来越多,如果设计上 稍有不慎就会出现模块之间相互依赖的情况。这对于使用Maven的用户是比较痛苦的,因为出现模块之间相互依赖的话在构建的时候就会失败,Maven通常要先编译被依赖的模块,如果出现相互依赖Maven就不知道该怎么办了。下图描述了三个Maven模块相互依赖的场景: 

image.png

 

 

 图中模块C依赖于模块B,模块B依赖于模块A,而模块A又依赖于模块C,这样就出现了相互依赖情况,如果运行mvn compile会出现如下错误:
[INFO] Scanning for projects… [ERROR] The projects in the reactor contain a cyclic reference: Edge between 'Ve rtex{label='org.kuuyee.sample:module-C:1.0-SNAPSHOT'}' and 'Vertex{label='org.ku uyee.sample:module-B:1.0-SNAPSHOT'}' introduces to cycle in the graph org.kuuyee .sample:module-B:1.0-SNAPSHOT –> org.kuuyee.sample:module-A:1.0-SNAPSHOT –> or g.kuuyee.sample:module-C:1.0-SNAPSHOT –> org.kuuyee.sample:module-B:1.0-SNAPSHO T -> [Help 1][ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e swit ch.[ERROR] Re-run Maven using the -X switch to enable full debug logging.[ERROR] [ERROR] For more information about the errors and possible solutions, please rea d the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectCycleEx ception
1. 使用build-helper-maven-plugin解决相互依赖的问题我的解决办法就是先把相互依赖的模块整合在一起,相当于把这些模块合并成一个单独的模块统一编译,
image.png

这样就产生了一个合并模块D,我们把它当做一个辅助构建模块,然后让A、B、C模块都依赖于D模块,这样的话就可以成功编译A、B和C模块,

 

 

 image.png

 

 要想把A、B、C三个模块整合在一起编译,需要借助build-helper-maven-plugin插件,这个插件在Maven构建周期提供一些辅助功能,下面列出插件的提供的功能列表: build-helper:add-source:添加更多的构建源码目录 build-helper:add-test-source:添加更多的测试源码目录 build-helper:add-resource:添加更多的资源目录 build-helper:add-test-resource:添加更多的测试资源目录 build-helper:attach-artifact:在安装和部署周期附加artifacts build-helper:maven-version:添加一个指定当前Maven版本的属性 build-helper:parse-version:添加一个指定组件版本的属性 build-helper:released-version:决定当前项目的最终版本 build-helper:remove-project-artifact:从本地资源库中移除项目的artifacts build-helper:reserve-network-port:Reserve a list of random and unused network ports. 在这里我们要用到build-helper:add-source这个功能,将模块A、B、C的源码路径加进来。 我们再添加一个辅助模块D,在辅助模块D中使用build-helper-maven-plugin插件,然后让模块A、B、C都依赖于辅助模块D,模块D的POM模型如下: 例 1. 辅助模块D的POM模型

java代码:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"  

  2.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  3.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  

  4.     <parent>  

  5.         <groupId>org.kuuyee.sample</groupId>  

  6.         <artifactId>sample-parent</artifactId>  

  7.         <version>1.0-SNAPSHOT</version>  

  8.         <relativePath>../../pom.xml</relativePath>  

  9.     </parent>  

  10.     <modelVersion>4.0.0</modelVersion>  

  11.     <groupId>org.kuuyee.sample</groupId>  

  12.     <artifactId>module-D</artifactId>  

  13.     <version>1.0-SNAPSHOT</version>  

  14.     <packaging>jar</packaging>  

  15.     <name>module-D</name>  

  16.     <url>http://maven.apache.org</url>  

  17.     <properties>  

  18.         <project.build.sourceEncoding>  

  19.             UTF-8  

  20.         </project.build.sourceEncoding>  

  21.         <module.a.src>../../module/module-A/src/main/java</module.a.src>  

  22.         <module.b.src>../../module/module-B/src/main/java</module.b.src>  

  23.         <module.c.src>../../module/module-C/src/main/java</module.c.src>  

  24.     </properties>  

  25.     <build>  

  26.         <plugins><!– 解决模块相互依赖,综合所有相互依赖代码统一编译 –>  

  27.             <plugin>  

  28.                 <groupId>org.codehaus.mojo</groupId>  

  29.                 <artifactId>build-helper-maven-plugin</artifactId>  

  30.                 <executions>  

  31.                     <execution>  

  32.                         <id>add-source</id>  

  33.                         <phase>generate-sources</phase>  

  34.                         <goals>  

  35.                             <goal>add-source</goal>  

  36.                         </goals>  

  37.                         <configuration>  

  38.                             <sources>  

  39.                                 <source>${module.a.src}</source>  

  40.                                 <source>${module.b.src}</source>  

  41.                                 <source>${module.c.src}</source>  

  42.                             </sources>  

  43.                         </configuration>  

  44.                     </execution>  

  45.                 </executions>  

  46.             </plugin>  

  47.         </plugins>  

  48.     </build>  

  49.     <dependencies>  

  50.         <dependency>  

  51.             <groupId>junit</groupId>  

  52.             <artifactId>junit</artifactId>  

  53.             <version>3.8.1</version>  

  54.             <scope>test</scope>  

  55.         </dependency>  

  56.     </dependencies>  

  57. </project>  

maven处理循环依赖

在多maven工程的项目里,如果工程间存在循环依赖,构建就会报错。本文介绍一下循环依赖要怎么处理

1、什么是循环依赖

  如果工程A依赖工程B,工程B又依赖工程A,就会形成循环依赖。或者A依赖B,B依赖C,C依赖A,也是循环依赖
  
  总的来说,在画出工程依赖图之后,如果发现工程间的依赖连线形成了一个有向循环图,则说明有循环依赖的现象
  
  如果循环依赖发生在工程之间,则会影响构建,因为maven不知道应该先编译哪个工程。如果循环依赖发生在同一个工程的模块之间,虽然不影响编译,但是也是一种不好的实践,说明模块的设计有问题,应该避免
  
  如果在模块内部,有几个类互相调用的话,我觉得可能是正常的。比如观察者模式里面,Observer和Observable就是互相依赖的

2、怎么解决循环依赖

  目前知道有2个办法可以解决
  
  第一个办法是用build-helper-maven-plugin插件来规避。比如A依赖B,B依赖C,C依赖A的情况。这个插件提供了一种规避措施,即临时地将工程A、B、C合并成一个中间工程,编译出临时的模块D。然后A、B、C再分别依赖临时模块D进行编译
  
  这种方法可以解决无法构建的问题,但是只是一个规避措施,工程的依赖关系依然是混乱的
  
  第二个办法是通过重构,从根本上消除循环依赖

3、如何重构

  目前也知道2个重构的思路
  
  第一个办法是平移,比如A和B互相依赖,那么可以将B依赖A的那部分代码,移动到工程B中,这样一来,B就不需要继续依赖A,只要A依赖B就可以了,从而消除循环依赖
  
  第二个办法是下移,比如A和B互相依赖,同时它们都依赖C,那么可以将B和A相互依赖的那部分代码,移动到工程C里,这样一来,A和B相互之间都不依赖,只继续依赖C,也可以消除循环依赖
  
  这两种重构方式都是可行的,具体采用哪种方式要根据实际情况来判断。不管采取哪种方式,都需要对代码进行修改,有时候并不是那么容易的