创建 maven 多模块工程并解决子模块之间循环依赖问题

背景:

       想在 eclipse 中创建一个拥有子模块的 maven 工程,因为在实际项目中用到的就是多模块工程,原谅我不知道这个工程是如何创建的,因为以前没有接触过,在这里记录下创建工程。

创建父 maven 工程:

       打开 eclipse右键 ---- New ---- Maven Project ,如下图所示:

创建 maven 多模块工程并解决子模块之间循环依赖问题_第1张图片

       这个 Add project(s) to working set 的意思是把一些相关的项目归类到一起,任何项目都可以归类。这个选不选没啥意义,点击 next 。

创建 maven 多模块工程并解决子模块之间循环依赖问题_第2张图片

        需要注意的是,这个 Packaging 要选择 pom 类型,然后点击 Finish 。

创建 maven 多模块工程并解决子模块之间循环依赖问题_第3张图片

        因为这是个父工程,不需要什么源码,所以进入到工程的目录下,把不用的目录都删除掉,仅留下 pom.xml 即可,如下图所示:

创建 maven 多模块工程并解决子模块之间循环依赖问题_第4张图片

       最后这个父工程的样子,就长成这样:

创建子 maven 工程: 

       我们在父工程项目上 右键 ---- New ---- Other ---- Maven Module ,如下所示:

创建 maven 多模块工程并解决子模块之间循环依赖问题_第5张图片

       点击 next ,输入子工程的项目名称,如下所示:

创建 maven 多模块工程并解决子模块之间循环依赖问题_第6张图片

       继续点击 next 。点击 Finish 即可 。 

创建 maven 多模块工程并解决子模块之间循环依赖问题_第7张图片

        最终形成的工程目录结构如下所示:

创建 maven 多模块工程并解决子模块之间循环依赖问题_第8张图片

        为了方便后面说明如何添加子工程之间的依赖,我们再创建一个子工程,具体的操作步骤和上面的一样,我这里只把最终的工程目录的结构图贴出来,如下所示:

创建 maven 多模块工程并解决子模块之间循环依赖问题_第9张图片

       此时,若我们想在 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 依赖 BB 依赖 CC 依赖 A,也是所谓的循环依赖 。总的来说,在画出工程依赖图之后,如果发现工程间的依赖连线形成了一个有向循环图,则说明有循环依赖的现象 。

       如果循环依赖发生在工程之间,则会影响构建,因为 maven 不知道应该先编译哪个工程。如果循环依赖发生在同一个工程的模块之间,虽然不影响编译,但是也是一种不好的实践,说明模块的设计有问题,应该避免 。

       如果这种情况发生在模块内部,有几个类互相调用的话,我觉得可能是正常的。比如观察者模式里面,Observer Observable 就是互相依赖的。这个没啥问题。 

解决循环依赖问题:

方式一:使用 build-helper-maven-plugin 插件来规避

       若发生 A 依赖 BB 依赖 A 的情况。这个插件提供了一种规避措施,即临时地将工程 AB 合并成一个中间工程,编译出临时的模块 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  
                              
                            
                                ${module.a.src}  
                                ${module.b.src}  
                              
                          
                      
                  
              
          
     

        我们再创建一个子工程 springboot_dubbo_start 用于启动所有的模块,现在整个工程的目录结构如下所示:

创建 maven 多模块工程并解决子模块之间循环依赖问题_第10张图片

       在 springboot_dubbo 中配置 springboot 的父 maven 依赖,不能在子模块中添加这个依赖,因为子模块中已经有了   标签,这个标签在一个 pom.xml 中只能有一个。pom.xml 的内容如下所示:


	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=worldhttp://localhost:8080/hello2?name=world 进行测试,可以正常的启动和访问,当然了,构建版本也是没有问题的。

注意:

        发现了一个小 bug ,就是当我修改了 springboot_dubbo_provider 和 springboot_dubbo_customer 项目中的 Controller 代码时,重启工程,发现展示的效果还是未修改之前的效果。当我把整个工程 maven-cleanmaven-install 之后,再重启工程,就是修改后的效果了。可能时由于我们中间依赖了一个中间模块 springboot_dubbo_app 造成的。这点在使用的时候注意点。

方式二:重构

       第一个办法是平移,比如 AB 互相依赖,那么可以将 B 依赖 A 的那部分代码,移动到工程 B 中,这样一来,B 就不需要继续依赖 A,只要 A 依赖 B 就可以了,从而消除循环依赖 。

       第二个办法是下移,比如 AB 互相依赖,同时它们都依赖 C,那么可以将 BA 相互依赖的那部分代码,移动到工程 C 里,这样一来,A B 相互之间都不依赖,只继续依赖 C ,也可以消除循环依赖。

       这两种重构方式都是可行的,具体采用哪种方式要根据实际情况来判断。不管采取哪种方式,都需要对代码进行修改,有时候并不是那么容易的 。

你可能感兴趣的:(Maven)