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

背景

之前做的一个巡检机器人项目,硬件分3大块,如图
多模块maven工程,Spring模块和非Spring模块如何共存?_第1张图片
树莓派通过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配置


<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.0modelVersion>

    <groupId>com.lanplus.lanovagroupId>
    <artifactId>lanovaartifactId>
    <packaging>pompackaging>
    <version>1.0-SNAPSHOTversion>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>8source>
                    <target>8target>
                configuration>
            plugin>
        plugins>
    build>
    <modules>
        <module>lanova-camera_controlmodule>
        <module>lanova-chassis_controlmodule>
    modules>

    <dependencies>
        <dependency>
            <groupId>com.google.code.gsongroupId>
            <artifactId>gsonartifactId>
            <version>2.7version>
            
        dependency>
    dependencies>
project>

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

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


<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.lanovagroupId>
        <artifactId>lanovaartifactId>    
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>lanova-chassis_controlartifactId>

    <dependencies>
        <dependency>
            <groupId>com.google.code.gsongroupId>
            <artifactId>gsonartifactId>
        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配置:


<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.0modelVersion>
	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.2.5.RELEASEversion>
		<relativePath/> 
	parent>
	<groupId>com.lanplus.lanovagroupId>
	<artifactId>lanova-webartifactId>
	<version>0.0.1-SNAPSHOTversion>
	<name>lanova-webname>
	<description>lanova-webdescription>

	<dependencies>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-devtoolsartifactId>
			<scope>runtimescope>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-configuration-processorartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>org.projectlombokgroupId>
			<artifactId>lombokartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
			<scope>testscope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintagegroupId>
					<artifactId>junit-vintage-engineartifactId>
				exclusion>
			exclusions>
		dependency>
	dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			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文件,截取一部分给大家看看

<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.0modelVersion>
  <groupId>org.springframework.bootgroupId>
  <artifactId>spring-boot-dependenciesartifactId>
  <version>2.2.5.RELEASEversion>
  <packaging>pompackaging>
  <name>Spring Boot Dependenciesname>
  <description>Spring Boot Dependenciesdescription>
  <url>https://projects.spring.io/spring-boot/#url>
  <licenses>
    <license>
      <name>Apache License, Version 2.0name>
      <url>https://www.apache.org/licenses/LICENSE-2.0url>
    license>
  licenses>
  <developers>
    <developer>
      <name>Pivotalname>
      <email>[email protected]email>
      <organization>Pivotal Software, Inc.organization>
      <organizationUrl>https://www.spring.ioorganizationUrl>
    developer>
  developers>
  <scm>
    <url>https://github.com/spring-projects/spring-booturl>
  scm>
  <properties>
    <lombok.version>1.18.12lombok.version>
  properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-bootartifactId>
        <version>2.2.5.RELEASEversion>
      dependency>
      <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <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配置


<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.5.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.lanplus.lanovagroupId>
    <artifactId>lanovaartifactId>
    <packaging>pompackaging>
    <version>1.0-SNAPSHOTversion>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>8source>
                    <target>8target>
                configuration>
            plugin>
        plugins>
    build>
    <modules>
        <module>lanova-camera_controlmodule>
        <module>lanova-chassis_controlmodule>
        <module>lanova-webmodule>
    modules>

    <dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.google.code.gsongroupId>
            <artifactId>gsonartifactId>
            <version>2.7version>
            <scope>compilescope>
        dependency>
        
    dependencies>
    dependencyManagement>
project>

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


<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.0modelVersion>
	<parent>
		<artifactId>lanovaartifactId>
		<groupId>com.lanplus.lanovagroupId>
		<version>1.0-SNAPSHOTversion>
	parent>
	<groupId>com.lanplus.lanovagroupId>
	<artifactId>lanova-webartifactId>
	<version>0.0.1-SNAPSHOTversion>
	<name>lanova-webname>
	<description>lanova-webdescription>

	<dependencies>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-devtoolsartifactId>
			<scope>runtimescope>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-configuration-processorartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>org.projectlombokgroupId>
			<artifactId>lombokartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
			<scope>testscope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintagegroupId>
					<artifactId>junit-vintage-engineartifactId>
				exclusion>
			exclusions>
		dependency>
		<dependency>
			<groupId>com.lanplus.lanovagroupId>
			<artifactId>lanova-chassis_controlartifactId>
			<version>1.0-SNAPSHOTversion>
		dependency>
        <dependency>
            <groupId>com.lanplus.lanovagroupId>
            <artifactId>lanova-chassis_controlartifactId>
            <version>1.0-SNAPSHOTversion>
        dependency>
        <dependency>
            <groupId>com.lanplus.lanovagroupId>
            <artifactId>lanova-camera_controlartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>compilescope>
        dependency>
    dependencies>

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

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

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

总结

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

你可能感兴趣的:(Java)