详解Maven多模块Spring Boot项目从创建到打包
文章目录
- 详解Maven多模块Spring Boot项目从创建到打包
- 多模块的优点
- 示例项目简介
- 环境准备
- 详解从创建到打包
- 遇到的坑及其解决方法
- 特别说明
详解Maven多模块Spring Boot项目从创建到打包
多模块的优点
在平常的项目开发中,我们一直在追求“高内聚,低耦合”——解耦;从小到函数,再到类,再到模块,都是“解耦”思想的产物,解耦有诸多好处,多模块正是解耦的一种手段;多模块的设计包含但不限于以下好处:
- 提高开发效率:在开发过程中,将一个项目划分为多个模块,有效地实现并行开发,大大提高了开发效率;
- 便于维护、调试:在测试、维护过程中,模块对应着职责,职责越分明,调试、维护、修改就越容易定位;
- 便于编译、部署:当项目代码发生变化时,可以只更新编译相应的模块,而无需编译整个项目,显著提高大型项目编译和部署的效率;
- 便于代码重用:因为模块之间的相对独立性使得模块可以得到最大程度的重用;也从另一角度提高了项目开发的效率;
示例项目简介
示例项目有一个父项目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
详解从创建到打包
通过Spring Initializr创建Spring Boot父项目:
修改信息:groupId:com.xiaomo.demo;artifactId:product;打包方式可以选择jar(我这里没有pom选项,不过不影响);父项目结构:
删掉.mvn、mvnw、mvnw.cmd——暂时用不到,就删了吧:
添加第一个子模块:
选择Maven项目:
这里选择Maven项目是因为当添加的子模块为Maven项目时,IDEA会自动修改product的pom文件:添加Modules节点、修改打包方式为pom;然而,当添加的模块为SpringBoot时,并没有这些动作;而实际上,SpringBoot项目本身也是Maven管理的,所以并无大碍;
子模块坐标信息:
添加完该模块后,父pom的内容:
添加SpringBoot子模块:
添加Spring Boot模块是因为该模块可以独立作为一个Spring Boot项目开发,然后整合到大工程里;添加完Spring Boot模块后父pom的内容:
可以看到,没有什么变化,所以需要按照common模块,建立父子关系;同样,添加reader模块;总体目录结构:
这里,还是删除了mvn等暂时用不到的文件;在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>
直接运行product的Install会失败:
很明显,blog依赖于common的User,但是在编译blog的时候,找不到User,实际上,你可以在IDEA里通过启动blog的main函数来运行blog,但是blog不能打包;修改common(被依赖模块)的pom文件(去掉Common里的注释)
运行Install,命令行测试Reader模块的Register方法:
本地Maven仓库:
至此,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时调用的插件依次为:
- maven-resources-plugin:3.1.0:resources (default-resources);
- maven-compiler-plugin:3.8.0:compile (default-compile);
- maven-resources-plugin:3.1.0:testResources (default-testResources);
- maven-compiler-plugin:3.8.0:testCompile (default-testCompile);
- maven-surefire-plugin:2.22.1:test (default-test) ;
- maven-jar-plugin:3.1.1:jar (default-jar) ;
而Spring Boot项目上运行package时调用的插件依次为:
- maven-resources-plugin:3.1.0:resources (default-resources);
- maven-compiler-plugin:3.8.0:compile (default-compile);
- maven-resources-plugin:3.1.0:testResources (default-testResources);
- maven-compiler-plugin:3.8.0:testCompile (default-testCompile);
- maven-surefire-plugin:2.22.1:test (default-test) ;
- maven-jar-plugin:3.1.1:jar (default-jar) ;
- 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项目从创建到打包相关推荐
- source tree 递归子模块_多模块 Spring Boot 项目
在开发一个稍具规模的项目时,我们常常希望能够将程序的各个部分分成不同的模块,加以管理.本完简单介绍了如何建立一个多模块的 Spring Boot 项目. 假设我们需要开发一个包含 2 个子模块的项目: ...
- 使用maven构建的Spring boot项目在开始搭建的时候出的一些错误
首先,先构建一个maven项目,构建好了之后,会有一个错误,先说明一下,我生成maven项目时jre给我自动选择的是1.5版本的,而本人安装的是1.8的 然后,我们要消除这个错误,发现是缺少了一个ja ...
- Spring Boot 项目总是创建失败,这几个备选方案一定要收藏
作者 | 江南一点雨 责编 | 欧阳姝黎 松哥这两天老遇到一个问题,用 IDEA 创建 Spring Boot 项目总是失败,搞得我很郁闷,一气之下收集了几个可用的 Spring Boot ...
- Myeclipse2017下使用Maven搭建的spring boot项目怎样运行?
刚开始接触SpringBoot以及Maven,在网上找到很多入门Demo,将这些Demo在Myecplise中导入后怎样运行? ①导入到Myecplise ②右击项目名-->Maven--> ...
- spring boot项目精简瘦身打包
1.背景介绍 当网络不是很给力的时候,一个大的jar包传输是费时的,往往存在修改1分钟,传包10分钟的尴尬时刻. 1)正常打包出来的jar 2)解压出来后的包大小98M 3)lib的jar包大小 综上 ...
- 我们公司使用了6年的Spring Boot项目部署方案,打包 + 一键部署,稳的一批
时间就如白驹过隙,转眼间已经是 2028 年了.小二入职一家初创公司已经 6 年了,眼瞅着开发团队从 3 个人壮大到 54 人,心里有时候会感觉挺不可思议的. 这些年,身边的同事来了又去,有些刚熟悉没 ...
- 创建并运行一个 Spring Boot 项目
创建并运行一个 Spring Boot 项目 引言 第一个 Spring Boot 项目 1. 创建一个 spring boot 项目 第一步 第二步 第三步 第四步 2. 验证 第一步 第二步 3. ...
- Spring Boot项目(Maven\Gradle)三种启动方式及后台运行详解
Spring Boot项目三种启动方式及后台运行详解 1 Spring Boot项目三种启动方法 运行Application.java类中的Main方法 项目管理工具启动 Maven项目:mvn sp ...
- docker添加新的环境变量_Docker的安装及部署Spring Boot项目操作详解!
本文使用Docker部署Spring Boot项目.部署之前需要环境中已经安装Docker和Maven(用于打包),所以本文先进行安装Docker和Maven:接着搭建一个Spring Boot项目, ...
- maven web项目导入sts_Spring Boot2 系列教程(二)创建 Spring Boot 项目的三种方式
我最早是 2016 年底开始写 Spring Boot 相关的博客,当时使用的版本还是 1.4.x ,文章发表在 CSDN 上,阅读量最大的一篇有 43W+,如下图: 2017 年由于种种原因,就没有 ...
最新文章
- 数字汽车钥匙的安全性增强技术
- (C++)自定义链表并写入
- java中,字符串和集合判断是否为空
- [git] 如何处理push失败的commit
- Tomcat虚拟目录的配置
- C语言中static用法大全
- extjs tree下拉列表_使用ztree来代替Extjs的下拉树
- mysql忘记了密码、允许远程连接、mysql卸载 -- linux
- 你要的六级成绩批量查询,它来啦......
- HDU3501——欧拉函数裸题
- 微信小程序简单论坛实现demo,供参考。
- 基于simulink的模糊PID控制器设计与仿真
- 强大的头像制作神器微信小程序
- Centos 普通用户su到root用户提示错误
- SAP R3 功能详解 - 固定资产会计
- ERROR: Failed building wheel for ctcdecode
- 2021乐平四中高考成绩查询,喜报!乐平高考成绩出炉!2019再创佳绩!!
- java的tey语句return了_Java finally语句到底是在return之前还是之后执行?
- Windows在与time.windows.com进行同步时出错。
- 怎么在线对视频进行压缩?