想在 eclipse 中创建一个拥有子模块的 maven 工程,因为在实际项目中用到的就是多模块工程,原谅我不知道这个工程是如何创建的,因为以前没有接触过,在这里记录下创建工程。
打开 eclipse,右键 ---- New ---- Maven Project ,如下图所示:
这个 Add project(s) to working set 的意思是把一些相关的项目归类到一起,任何项目都可以归类。这个选不选没啥意义,点击 next 。
需要注意的是,这个 Packaging 要选择 pom 类型,然后点击 Finish 。
因为这是个父工程,不需要什么源码,所以进入到工程的目录下,把不用的目录都删除掉,仅留下 pom.xml 即可,如下图所示:
最后这个父工程的样子,就长成这样:
我们在父工程项目上 右键 ---- New ---- Other ---- Maven Module ,如下所示:
点击 next ,输入子工程的项目名称,如下所示:
继续点击 next 。点击 Finish 即可 。
最终形成的工程目录结构如下所示:
为了方便后面说明如何添加子工程之间的依赖,我们再创建一个子工程,具体的操作步骤和上面的一样,我这里只把最终的工程目录的结构图贴出来,如下所示:
此时,若我们想在 springboot_dubbo_customer 项目中,引入 springboot_dubbo_provider 项目的依赖,相应的 pom.xml 内容如下所示:
4.0.0
com
springboot_dubbo
0.0.1-SNAPSHOT
springboot_dubbo_customer
com
springboot_dubbo_provider
0.0.1-SNAPSHOT
此时发生这样一种情况,即 springboot_dubbo_customer 项目依赖于 springboot_dubbo_provider 项目,而 springboot_dubbo_provider 又依赖于 springboot_dubbo_customer 项目,就会发生循环依赖,程序就无法进行进行编译,导致项目报错。
如果工程 A 依赖工程 B,工程 B 又依赖工程 A,就会形成循环依赖。或者 A 依赖 B,B 依赖 C,C 依赖 A,也是所谓的循环依赖 。总的来说,在画出工程依赖图之后,如果发现工程间的依赖连线形成了一个有向循环图,则说明有循环依赖的现象 。
如果循环依赖发生在工程之间,则会影响构建,因为 maven 不知道应该先编译哪个工程。如果循环依赖发生在同一个工程的模块之间,虽然不影响编译,但是也是一种不好的实践,说明模块的设计有问题,应该避免 。
如果这种情况发生在模块内部,有几个类互相调用的话,我觉得可能是正常的。比如观察者模式里面,Observer 和 Observable 就是互相依赖的。这个没啥问题。
方式一:使用 build-helper-maven-plugin 插件来规避
若发生 A 依赖 B,B 依赖 A 的情况。这个插件提供了一种规避措施,即临时地将工程 A 和 B 合并成一个中间工程,编译出临时的模块 D。然后 A 和 B 再分别依赖临时模块 D 进行编译。这种方法可以解决无法构建的问题,但是只是一个规避措施,工程的依赖关系依然是混乱的。
针对于这种解决方案,在我们上面的项目中再创建一个子模块工程 springboot_dubbo_app 作为中间工程,创建过程省略,只贴出这个中间模块的 pom.xml,因为这个工程除了这个配置文件其他什么内容都没有,配置文件内容如下所示:
4.0.0
com
springboot_dubbo
0.0.1-SNAPSHOT
../pom.xml
springboot_dubbo_app
jar
springboot_dubbo_app
http://maven.apache.org
UTF-8
../springboot_dubbo_customer/src/main/java
../springboot_dubbo_provider/src/main/java
org.codehaus.mojo
build-helper-maven-plugin
add-source
generate-sources
add-source
我们再创建一个子工程 springboot_dubbo_start 用于启动所有的模块,现在整个工程的目录结构如下所示:
在 springboot_dubbo 中配置 springboot 的父 maven 依赖,不能在子模块中添加这个依赖,因为子模块中已经有了
4.0.0
com
springboot_dubbo
0.0.1-SNAPSHOT
pom
springboot_dubbo_provider
springboot_dubbo_customer
springboot_dubbo_app
springboot_dubbo_start
org.springframework.boot
spring-boot-starter-parent
1.5.8.RELEASE
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
在 springboot_dubbo_customer 子项目中配置其依赖于中间模块 springboot_dubbo_app ,完整的 pom.xml 内容如下所示:
4.0.0
com
springboot_dubbo
0.0.1-SNAPSHOT
springboot_dubbo_customer
springboot_dubbo_customer
com
springboot_dubbo_app
0.0.1-SNAPSHOT
添加一个 Controller 类用于测试项目能否正常编译和访问,代码如下所示:
package com;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloWorld2 {
@RequestMapping("/hello2")
public String Hello2(String name) {
return "Hellow"+name;
}
}
在 springboot_dubbo_provider 子项目中配置其依赖于中间模块 springboot_dubbo_app ,完整的 pom.xml 内容如下所示:
4.0.0
com
springboot_dubbo
0.0.1-SNAPSHOT
springboot_dubbo_provider
springboot_dubbo_provider
com
springboot_dubbo_app
0.0.1-SNAPSHOT
添加一个 Controller 类用于测试项目能否正常编译和访问,代码如下所示:
package com;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloWorld {
@RequestMapping("/hello")
public String Hello(String name) {
return "Hello"+name;
}
}
在 springboot_dubbo_start 子项目中配置其依赖于所有的子模块,方便启动所有的子模块工程,完整的 pom.xml 内容如下所示:
4.0.0
com
springboot_dubbo
0.0.1-SNAPSHOT
springboot_dubbo_start
com
springBoot_dubbo_customer
0.0.1-SNAPSHOT
com
springBoot_dubbo_provider
0.0.1-SNAPSHOT
添加一个启动类 App,代码如下所示:
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
}
启动工程,在浏览器中输入 http://localhost:8080/hello?name=world 和 http://localhost:8080/hello2?name=world 进行测试,可以正常的启动和访问,当然了,构建版本也是没有问题的。
注意:
发现了一个小 bug ,就是当我修改了 springboot_dubbo_provider 和 springboot_dubbo_customer 项目中的 Controller 代码时,重启工程,发现展示的效果还是未修改之前的效果。当我把整个工程 maven-clean,maven-install 之后,再重启工程,就是修改后的效果了。可能时由于我们中间依赖了一个中间模块 springboot_dubbo_app 造成的。这点在使用的时候注意点。
方式二:重构
第一个办法是平移,比如 A 和 B 互相依赖,那么可以将 B 依赖 A 的那部分代码,移动到工程 B 中,这样一来,B 就不需要继续依赖 A,只要 A 依赖 B 就可以了,从而消除循环依赖 。
第二个办法是下移,比如 A 和 B 互相依赖,同时它们都依赖 C,那么可以将 B 和 A 相互依赖的那部分代码,移动到工程 C 里,这样一来,A 和 B 相互之间都不依赖,只继续依赖 C ,也可以消除循环依赖。
这两种重构方式都是可行的,具体采用哪种方式要根据实际情况来判断。不管采取哪种方式,都需要对代码进行修改,有时候并不是那么容易的 。