文章目录

  • 详解Maven多模块Spring Boot项目从创建到打包
    • 多模块的优点
    • 示例项目简介
    • 环境准备
    • 详解从创建到打包
    • 遇到的坑及其解决方法
    • 特别说明

详解Maven多模块Spring Boot项目从创建到打包

多模块的优点

在平常的项目开发中,我们一直在追求“高内聚,低耦合”——解耦;从小到函数,再到类,再到模块,都是“解耦”思想的产物,解耦有诸多好处,多模块正是解耦的一种手段;多模块的设计包含但不限于以下好处:

  1. 提高开发效率:在开发过程中,将一个项目划分为多个模块,有效地实现并行开发,大大提高了开发效率;
  2. 便于维护、调试:在测试、维护过程中,模块对应着职责,职责越分明,调试、维护、修改就越容易定位;
  3. 便于编译、部署:当项目代码发生变化时,可以只更新编译相应的模块,而无需编译整个项目,显著提高大型项目编译和部署的效率;
  4. 便于代码重用:因为模块之间的相对独立性使得模块可以得到最大程度的重用;也从另一角度提高了项目开发的效率;

示例项目简介

示例项目有一个父项目product+三个子模块:blog、reader以及common构成;其中common模块提供基础的服务(这里是向整个项目贡献了一个User类),blog和reader模块的注册功能依赖于该类;而blog和reader模块在示例项目里没有分别,属于平级;

常见的多模块设计方式为:表示层模块(web)+持久层(persistent)+模型层(model);在这里,我将项目划分为博客系统(blog)以及阅读器系统(reader)以及功能系统(common),因为重点在于说明Spring Boot多模块项目的构建,所以每个子系统并不会做详细的设计;如果要继续开发的话,我会选择:controller+service+model+dto+repository这样的方式设计每个子系统,以实现前后端分离的目的;

实际上,这种系统架构还是属于垂直的MVC架构,只是实现方式因人、因项目而异;对于复杂系统,分布式的架构会成为最佳选择;

值得说明的是,这几个模块都算是Spring Boot项目,也就是父项目的打包插件将是Spring Boot提供的,而非Maven提供的;这会有很多好玩(痛苦)的坑;如果子模块是Maven项目,倒也平淡(顺利)了许多;

环境准备

操作系统:Windows8.1

JDK:1.8

IDE:IntellijIDEA

Maven:3.5.4

详解从创建到打包

  1. 通过Spring Initializr创建Spring Boot父项目:

    修改信息:groupId:com.xiaomo.demo;artifactId:product;打包方式可以选择jar(我这里没有pom选项,不过不影响);

  2. 父项目结构:

  3. 删掉.mvn、mvnw、mvnw.cmd——暂时用不到,就删了吧:

  4. 添加第一个子模块:

  5. 选择Maven项目:

    这里选择Maven项目是因为当添加的子模块为Maven项目时,IDEA会自动修改product的pom文件:添加Modules节点、修改打包方式为pom;然而,当添加的模块为SpringBoot时,并没有这些动作;而实际上,SpringBoot项目本身也是Maven管理的,所以并无大碍;

  6. 子模块坐标信息:

  7. 添加完该模块后,父pom的内容:

  8. 添加SpringBoot子模块:


    添加Spring Boot模块是因为该模块可以独立作为一个Spring Boot项目开发,然后整合到大工程里;

  9. 添加完Spring Boot模块后父pom的内容:

    可以看到,没有什么变化,所以需要按照common模块,建立父子关系;同样,添加reader模块;

  10. 总体目录结构:

    这里,还是删除了mvn等暂时用不到的文件;

  11. 在common模块里添加User类,处于com.xiaomo.demo.common包;这是Spring Boot项目默认的包:groupID+artifactID;并在reader和blog模块里添加对common模块的依赖,分别创建RegisterService以及RegisterController类;
    代码如下:

    package com.xiaomo.demo.common;
    public class User {private String name;private boolean registered;public User(){}public User(String name){this.name=name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public boolean isRegistered() {return registered;}public void setRegistered(boolean registered) {this.registered = registered;}
    }
    package com.xiaomo.demo.reader;
    @Service
    public class RegisterService {public User register(User user){user.setRegistered(true);System.out.println("Reader System Register:"+user.getName());return user;}
    }
    package com.xiaomo.demo.reader;
    @SpringBootApplication//(scanBasePackages = {"com.xiaomo.demo.*"})详见坑二
    public class ReaderApplication {public static void main(String[] args) {SpringApplication.run(ReaderApplication.class, args);}
    }
    

    pom文件如下:

    //product->pom.xml
    <?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><packaging>pom</packaging><modules><module>Common</module><module>Blog</module><module>Reader</module></modules><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.2.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.xiaomo.demo</groupId><artifactId>product</artifactId><version>0.0.1</version><name>product</name><description>Demo project for Spring Boot And Maven</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
    </project>//reader->pom.xml
    <?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><artifactId>product</artifactId><groupId>com.xiaomo.demo</groupId><version>0.0.1</version></parent><artifactId>reader</artifactId><name>reader</name><packaging>jar</packaging><description>Reader project for Spring Boot And Maven</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>com.xiaomo.demo</groupId><artifactId>common</artifactId><version>0.0.1</version></dependency></dependencies>
    </project>
    //common->pom.xml
    <?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.xiaomo.demo</groupId><artifactId>product</artifactId><version>0.0.1</version></parent><modelVersion>4.0.0</modelVersion><artifactId>common</artifactId><packaging>jar</packaging><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><!--注释以下代码,会出错--><!--<configuration><skip>true</skip></configuration>--></plugin></plugins></build>
    </project>
    
  12. 直接运行product的Install会失败:

    很明显,blog依赖于common的User,但是在编译blog的时候,找不到User,实际上,你可以在IDEA里通过启动blog的main函数来运行blog,但是blog不能打包;

  13. 修改common(被依赖模块)的pom文件(去掉Common里的注释)

  14. 运行Install,命令行测试Reader模块的Register方法:



    本地Maven仓库:

  15. 至此,Spring Boot多模块Maven项目成功构建完成;

遇到的坑及其解决方法

(一)明明添加了对Common模块的依赖,但是打包的时候还是会报找不到User类;

该问题在构建过程里也提到过,只是那时直接给出了解决方法,现在详细分析为什么会出现错误~

原因分析:首先,我们的父项目是Spring Boot项目,所以打包的时候使用的是spring-boot-maven-plugin插件,该插件是Spring Boot提供的用于构建Spring Boot项目的;而Spring Boot遵循“ 约定大于配置”,其所生成的jar包与maven默认插件生成的jar包的结构是不一样的,Spring Boot项目的jar包其类文件被放置在一个BOOT-INF文件夹里,这就是为什么添加了依赖,但是仍然找不到User类的原因;所以,我们需要以普通方式生成Common模块的jar包;而这是配置skip=true来实现的;

当我们使用Spring Boot插件打包Reader模块时,reader.jar的结构如下:


其中,BOOT-INF的内容如下:


我们的common.jar的确是在里面,当然,这是修改了common打包方式之后的结果,否则reader根本无法通过编译,也就谈不上打包了;

我们再去看看此时common.jar的结构:


相比reader的结构,是不是少了BOOT-INF文件夹?

通过观察,普通Maven项目上运行package时调用的插件依次为:

  1. maven-resources-plugin:3.1.0:resources (default-resources);
  2. maven-compiler-plugin:3.8.0:compile (default-compile);
  3. maven-resources-plugin:3.1.0:testResources (default-testResources);
  4. maven-compiler-plugin:3.8.0:testCompile (default-testCompile);
  5. maven-surefire-plugin:2.22.1:test (default-test) ;
  6. maven-jar-plugin:3.1.1:jar (default-jar) ;

而Spring Boot项目上运行package时调用的插件依次为:

  1. maven-resources-plugin:3.1.0:resources (default-resources);
  2. maven-compiler-plugin:3.8.0:compile (default-compile);
  3. maven-resources-plugin:3.1.0:testResources (default-testResources);
  4. maven-compiler-plugin:3.8.0:testCompile (default-testCompile);
  5. maven-surefire-plugin:2.22.1:test (default-test) ;
  6. maven-jar-plugin:3.1.1:jar (default-jar) ;
  7. spring-boot-maven-plugin:2.1.2.RELEASE:repackage (repackage);

由上可见,Spring Boot项目较普通Maven项目的特别之处在于,Spring Boot提供的插件将生成的jar包进行了重新打包;这也是BOOT-INF文件夹的来由;

(二)打包成功,启动Reader模块,可以使用User类,但是无法访问Common模块的Controller方法;

我们通过修改common模块打包方式的方法,使得common成为一个“正常”的jar包;但是如果此时运行reader.jar,你就会发现无法访问Common的Controller方法~ 这就是“正常”的代价;

这种情况在我的另一篇博客里有提到,只不过那时是因为找不到实体类;但是基本原理是一样的:Spring Boot通过IOC容器管理Controller等Bean,但是reader模块里的Spring Boot不知道Common模块的方法;这是因为Spring Boot默认的扫描包为启动方法所在的包,而Common的Controller不在 Reader的扫描包里;解决方法就是告诉Spring Boot需要扫描的包即可:@SpringBootApplication(scanBasePackages = {“com.xiaomo.demo.*”});因为前面是我们父项目的包名,所以当reader启动的时候,项目里所有的Bean都会被管理,自然也就可以访问啦;

特别说明

出现这种情况是因为我们的子模块本身需要使用Spring Boot提供的插件来打包(每个模块可单独运行),而子模块之间又存在依赖,此时Spring Boot的打包方式使得jar文件结构满足Spring Boot项目的约定而无法被其他模块发现;所以就要求被依赖的模块使用普通Maven项目的打包方式;如果子模块不需要打包(即子模块为普通Maven模块),那么就没有这样的坑了;

然而,子模块到底需不需要打包成Spring Boot类型的jar?我是将一个模块视为一个系统,自然需要能够独立开发、独立运行,也就是有打包的需求;但是如果将一个模块为视为一个功能点集合,比如持久层、模型层,这样也就没有了打包的需求;这样的系统即便划分为了多模块,但仍旧是集中式的;

详解Maven多模块Spring Boot项目从创建到打包相关推荐

  1. source tree 递归子模块_多模块 Spring Boot 项目

    在开发一个稍具规模的项目时,我们常常希望能够将程序的各个部分分成不同的模块,加以管理.本完简单介绍了如何建立一个多模块的 Spring Boot 项目. 假设我们需要开发一个包含 2 个子模块的项目: ...

  2. 使用maven构建的Spring boot项目在开始搭建的时候出的一些错误

    首先,先构建一个maven项目,构建好了之后,会有一个错误,先说明一下,我生成maven项目时jre给我自动选择的是1.5版本的,而本人安装的是1.8的 然后,我们要消除这个错误,发现是缺少了一个ja ...

  3. Spring Boot 项目总是创建失败,这几个备选方案一定要收藏

    作者 | 江南一点雨       责编 | 欧阳姝黎 松哥这两天老遇到一个问题,用 IDEA 创建 Spring Boot 项目总是失败,搞得我很郁闷,一气之下收集了几个可用的 Spring Boot ...

  4. Myeclipse2017下使用Maven搭建的spring boot项目怎样运行?

    刚开始接触SpringBoot以及Maven,在网上找到很多入门Demo,将这些Demo在Myecplise中导入后怎样运行? ①导入到Myecplise ②右击项目名-->Maven--> ...

  5. spring boot项目精简瘦身打包

    1.背景介绍 当网络不是很给力的时候,一个大的jar包传输是费时的,往往存在修改1分钟,传包10分钟的尴尬时刻. 1)正常打包出来的jar 2)解压出来后的包大小98M 3)lib的jar包大小 综上 ...

  6. 我们公司使用了6年的Spring Boot项目部署方案,打包 + 一键部署,稳的一批

    时间就如白驹过隙,转眼间已经是 2028 年了.小二入职一家初创公司已经 6 年了,眼瞅着开发团队从 3 个人壮大到 54 人,心里有时候会感觉挺不可思议的. 这些年,身边的同事来了又去,有些刚熟悉没 ...

  7. 创建并运行一个 Spring Boot 项目

    创建并运行一个 Spring Boot 项目 引言 第一个 Spring Boot 项目 1. 创建一个 spring boot 项目 第一步 第二步 第三步 第四步 2. 验证 第一步 第二步 3. ...

  8. Spring Boot项目(Maven\Gradle)三种启动方式及后台运行详解

    Spring Boot项目三种启动方式及后台运行详解 1 Spring Boot项目三种启动方法 运行Application.java类中的Main方法 项目管理工具启动 Maven项目:mvn sp ...

  9. docker添加新的环境变量_Docker的安装及部署Spring Boot项目操作详解!

    本文使用Docker部署Spring Boot项目.部署之前需要环境中已经安装Docker和Maven(用于打包),所以本文先进行安装Docker和Maven:接着搭建一个Spring Boot项目, ...

  10. maven web项目导入sts_Spring Boot2 系列教程(二)创建 Spring Boot 项目的三种方式

    我最早是 2016 年底开始写 Spring Boot 相关的博客,当时使用的版本还是 1.4.x ,文章发表在 CSDN 上,阅读量最大的一篇有 43W+,如下图: 2017 年由于种种原因,就没有 ...

最新文章

  1. 数字汽车钥匙的安全性增强技术
  2. (C++)自定义链表并写入
  3. java中,字符串和集合判断是否为空
  4. [git] 如何处理push失败的commit
  5. Tomcat虚拟目录的配置
  6. C语言中static用法大全
  7. extjs tree下拉列表_使用ztree来代替Extjs的下拉树
  8. mysql忘记了密码、允许远程连接、mysql卸载 -- linux
  9. 你要的六级成绩批量查询,它来啦......
  10. HDU3501——欧拉函数裸题
  11. 微信小程序简单论坛实现demo,供参考。
  12. 基于simulink的模糊PID控制器设计与仿真
  13. 强大的头像制作神器微信小程序
  14. Centos 普通用户su到root用户提示错误
  15. SAP R3 功能详解 - 固定资产会计
  16. ERROR: Failed building wheel for ctcdecode
  17. 2021乐平四中高考成绩查询,喜报!乐平高考成绩出炉!2019再创佳绩!!
  18. java的tey语句return了_Java finally语句到底是在return之前还是之后执行?
  19. Windows在与time.windows.com进行同步时出错。
  20. 怎么在线对视频进行压缩?

热门文章

  1. 眼保健操练习方法,眼保健操图解教程
  2. oracle 获取日期的毫秒_Oracle date timestamp 毫秒 - 时间函数总结(转)
  3. 自上而下 or 自下而上?企业部署RPA的2种策略
  4. 机器学习(六):特征降维和主成分分析法
  5. 我与北窗青年的2020丶
  6. 实现一个简单的记事本APP
  7. Chuck语言学习笔记——2.HelloWorld
  8. Bat 脚本学习 (基础篇)
  9. 学习单片机必须要学的八大知识点,你知道吗?
  10. ps钢笔路径打不出字怎么解决?