多模块maven工程,Spring模块和非Spring模块如何共存?

背景

之前做的一个巡检机器人项目,硬件分3大块,如图
硬件组成
树莓派通过USB网卡直连到底盘,树莓派和工业相机通过8口交换机实现互联。

机器人干的事就是在变电站自主导航,到预定点位后拍照,最后将照片上传到FTP服务器,供后台图像算法识别分析。

软件运行在树莓派上,采用Java开发,划分成3个模块,分别是

  1. 底盘控制系统
  2. 相机控制系统
  3. web交互系统

3个模块之间通过线程消息队列通信,这样既有高性能又有低耦合。

项目一开始我的规划是我只完成前2个,web交互系统交由专门做后台的同事,我将前2个打成jar包后交给他,他当作lib库调用即可。

多模块maven工程

好几年没写Java了,在简单学习了下mavenIDEA后,先开发底盘控制系统。主体开发完后,想加相机控制系统时,发现IDEA同一个窗口只能打开一个工程,蛋疼。

不想新开工程,因为这样代码就分散在2个git仓库了,于是将底盘和相机都降级为maven模块,原来的工程变成了POM工程(用在父级工程或聚合工程中,用来做jar工程的版本控制),而降级的maven模块仍是jar工程

父工程的pom配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lanplus.lanova</groupId>
    <artifactId>lanova</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <modules>
        <module>lanova-camera_control</module>
        <module>lanova-chassis_control</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.7</version>
            <!-- 其他公共依赖 -->
        </dependency>
    </dependencies>
</project>

可以看到,父工程的打包格式字段packagingpom,而不是单模块maven工程默认的jar

子模块底盘控制系统的pom配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
	    <groupId>com.lanplus.lanova</groupId>
        <artifactId>lanova</artifactId>    
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lanova-chassis_control</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
        <!-- 其他模块特有依赖 -->
    </dependencies>
</project>

可以看到,底盘控制系统降级成子模块后,增加了parent字段,里面记录了父工程lanova的maven信息,这样它本身的依赖项gson就不用指定版本信息了。

子模块相机控制系统的pom配置除了依赖有差异外,其他都跟底盘控制系统一样,就不贴了。

降级后的目录结构

lanova/
├── lanova-camera_control
│   ├── lanova-camera_control.iml
│   ├── pom.xml
│   ├── src
│   └── target
├── lanova-chassis_control
│   ├── lanova-chassis_control.iml
│   ├── pom.xml
│   ├── src
│   └── target
├── lanova.iml
├── pom.xml
└── src

这样重构后,代码组织结构清晰,将来引入其他传感器(例如热成像)也不慌,再加一个maven模块就行。另外子模块间能共享相同版本的依赖,比较省事。

SpringBoot:你管谁叫爸爸呢

相机控制系统的主体开发完后,通知做后台的同事着手开发web交互系统,没想到公司在紧急做一个疫情相关的项目,他脱不开身,我想着反正手头也没其他活儿,就自己搞吧。

询问了下后台同事 公司Java Web的技术选型,得知是SpringBoot,那我也保持一致吧。因为2008年后再也没用Java搞过Web开发,所以简单学习了下SpringBoot,初始代码基本照抄网上的,包括pom配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.lanplus.lanova</groupId>
	<artifactId>lanova-web</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>lanova-web</name>
	<description>lanova-web</description>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

可以看到,SpringBoot的版本号是2.2.5.RELEASE,它默认生成的模块pom配置指定org.springframework.bootspring-boot-starter-parent为自己的父工程,这样的好处是下面那些依赖项都不用指定版本号,因为都在父工程里指定好了。

虽然web模块的父亲跟其他模块不一样,但无所谓了,只要没有共同的依赖就行。开始在web里添加业务逻辑,其中一部分是跟底盘通信,需要lombok辅助实现对象与JSON互转,但是发现SpringBootlomboklanovalombok版本不一致,虽然不影响编译,但存在隐患,最好搞成一致的。

不过SpringBootlombok是不能改版本的,因为一旦SpringBoot的版本确定,它囊括的一堆库的相应版本也就确定了,具体见repository\org\springframework\boot\spring-boot-dependencies\2.2.5.RELEASE下的spring-boot-dependencies-2.2.5.RELEASE.pom文件,截取一部分给大家看看

<?xml version="1.0" encoding="utf-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.2.5.RELEASE</version>
  <packaging>pom</packaging>
  <name>Spring Boot Dependencies</name>
  <description>Spring Boot Dependencies</description>
  <url>https://projects.spring.io/spring-boot/#</url>
  <licenses>
    <license>
      <name>Apache License, Version 2.0</name>
      <url>https://www.apache.org/licenses/LICENSE-2.0</url>
    </license>
  </licenses>
  <developers>
    <developer>
      <name>Pivotal</name>
      <email>info@pivotal.io</email>
      <organization>Pivotal Software, Inc.</organization>
      <organizationUrl>https://www.spring.io</organizationUrl>
    </developer>
  </developers>
  <scm>
    <url>https://github.com/spring-projects/spring-boot</url>
  </scm>
  <properties>
    <lombok.version>1.18.12</lombok.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <version>2.2.5.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <build>
    <pluginManagement>
    </pluginManagement>
  </build>
</project>

可以看到,这也是个pom工程!并且在properties字段里指定了lombok等依赖的版本号,很明显,改这个pom文件是不明智的。

现在问题来了,到底该喊谁爸爸?即,所有子模块到底选谁做自己的父工程?

如果选lanova,则web模块无法使用springboot,不可接受。如果选springboot,则其他子模块一来要被迫跟Spring扯上关系,增加了不必要的耦合;二来其他子模块间本来通过lanova父工程共享的依赖,现在也没法挪到springboot里,只能每个工程单独定义一遍,容易不一致。

两全其美的解法

那就是将两代人整成三代人,爷爷、爸爸、孙子。因为pom模块也可以指定parent,就像类的继承层级,所以给父工程lanova添加parent字段,取值为springboot,同时将web模块的父工程改为lanova——跟底盘、相机一致——这样3个子模块都成了springboot的孙子,共享spring的web依赖版本,同时是lanova的儿子,共享lanova特定的gson、lombok依赖版本。

修改后的lanova工程pom配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lanplus.lanova</groupId>
    <artifactId>lanova</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <modules>
        <module>lanova-camera_control</module>
        <module>lanova-chassis_control</module>
        <module>lanova-web</module>
    </modules>

    <dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.7</version>
            <scope>compile</scope>
        </dependency>
        <!-- 其他公共依赖 -->
    </dependencies>
    </dependencyManagement>
</project>

注意看,修改前的lanova工程没有父工程,修改后父工程是springboot;另外lanova工程的依赖里并没有springMVC等spring特有的依赖,这些都放到web模块的新版pom配置里:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<artifactId>lanova</artifactId>
		<groupId>com.lanplus.lanova</groupId>
		<version>1.0-SNAPSHOT</version>
	</parent>
	<groupId>com.lanplus.lanova</groupId>
	<artifactId>lanova-web</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>lanova-web</name>
	<description>lanova-web</description>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>com.lanplus.lanova</groupId>
			<artifactId>lanova-chassis_control</artifactId>
			<version>1.0-SNAPSHOT</version>
		</dependency>
        <dependency>
            <groupId>com.lanplus.lanova</groupId>
            <artifactId>lanova-chassis_control</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.lanplus.lanova</groupId>
            <artifactId>lanova-camera_control</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

可以看到,在新版pom中,父工程从springboot变成了lanova,同时添加了对底盘、相机模块的依赖,这样web模块才能正常扮演全局调度的角色。

这样改还有个好处,底盘和相机的pom文件不用改动,他们还是认lanova做父亲,只是他们不知道自己已经是springboot的孙子了😉

总结

本方法非常适合包含各种子系统,并通过springboot向外界暴露出web接口的大型java项目。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值