目录
前面已经学习过Maven 工具的基本使用,如果需要开发一些中大型的项目,还需要学习 Maven 提供的一些高级的功能。Maven高级内容包括:分模块设计与开发、继承与聚合、私服。
分模块设计与开发
所谓分模块设计,顾名思义指的就是我们在设计一个 Java 项目的时候,将一个 Java 项目拆分成多个模块进行开发,每一个模块都是独立的。方便项目的管理维护、拓展,也方便模块键的相互调用、资源共享。
如果项目不分模块,也就意味着所有的业务代码都写在这一个 Java 项目当中,项目管理和维护起来将会非常的困难,项目中的通用组件也难以复用。
比如可以将商品的相关功能放在商品模块当中,搜索的相关业务功能我都封装在搜索模块当中等的,而为了组件的复用,我们也可以将项目当中的实体类、工具类以及我们定义的通用的组件都单独的抽取到一个模块当中,此时直接在订单模块当中引入工具类的坐标就可以了。
现在来看一下我们之前所开发的案例工程。可以看到在这个项目当中,除了我们所开发的部门管理以及员工管理、登录认证等相关业务功能以外,也定义了一些实体类,也就是pojo包下存放的一些类,像分页结果的封装类PageBean、 统一响应结果Result,我们还定义了一些通用的工具类,像Jwts、阿里云OSS操作的工具类等等。
如果在当前公司的其他项目组当中,也想使用我们所封装的这些公共的组件且直接依赖我们当前项目 tlias-web-management,项目在启动时将会把所有的类都加载进来,会影响性能 。也就意味着我们所有的业务代码都对外公开了,这个是非常不安全的。所以需要分模块设计。下面进行演示。
注意:分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分。此处只为了演示。
步骤:
- 将pojo包下的实体类,抽取到一个maven模块中 tlias-pojo
- 将utils包下的工具类,抽取到一个maven模块中 tlias-utils
- 其他的业务代码,放在tlias-web-management这个模块中,在该模块中需要用到实体类pojo、工具类utils,直接引入对应的依赖即可。
- 创建maven模块 tlias-pojo,存放实体类
A. 创建一个正常的Maven模块,模块名tlias-pojo
B. 然后在tlias-pojo中创建一个包 com.itheima.pojo (和原来案例项目中的pojo包名一致)
C. 将原来案例项目 tlias-web-management 中的pojo包下的实体类,复制到tlias-pojo模块中
D. 在 tlias-pojo 模块的pom.xml文件中引入需要的依赖
xml
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
E. 删除原有案例项目tlias-web-management的pojo包【直接删除不要犹豫,我们已经将该模块拆分出去了】,然后在pom.xml中引入 tlias-pojo的依赖
xml
<dependency>
<groupId>com.itheima</groupId>
<artifactId>tlias-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
- 创建Maven模块 tlias-utils,存放相关工具类
A. 创建一个正常的Maven模块,模块名tlias-utils
B. 然后在 tlias-utils 中创建一个包 com.itheima.utils (和原来案例项目中的utils包名一致)
C. 将原来案例项目 tlias-web-management 中的utils包下的实体类,复制到tlias-utils模块中
D. 在 tlias-utils 模块的pom.xml文件中引入需要的依赖
xml
<dependencies>
<!--JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!--阿里云OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
<!--WEB开发-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
E. 删除原有案例项目tlias-web-management的utils包【直接删除不要犹豫,我们已经将该模块拆分出去了】,然后在pom.xml中引入 tlias-utils的依赖
xml
<dependency>
<groupId>com.itheima</groupId>
<artifactId>tlias-utils</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
到此呢,就已经完成了模块的拆分,拆分出了 tlias-pojo、tlias-utils、tlias-webmanagement,如果其他项目中需要用到 pojo,或者 utils工具类,就可以直接引入依赖。
继承与聚合
在案例项目分模块开发之后,我们会看到tlias-pojo、tlias-utils、tlias-web-management中都引入了一个依赖 lombok 的依赖。我们在三个模块中分别配置了一次。
如果是做一个大型的项目,这三个模块当中重复的依赖可能会很多很多,功能虽然能实现,但是配置是比较繁琐的。可以用Maven 的继承来解决这个问题。
继承
我们可以再创建一个父工程 tlias-parent ,然后让上述的三个模块都来继承这个父工程 。 然后再将各个模块中都共有的依赖,都提取到父工程 tlias-parent中进行配置,只要子工程继承了父工程,依赖它也会继承下来,这样就无需在各个子工程中进行配置了。
-
概念:继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。
-
作用:简化依赖配置、统一管理依赖
-
实现:
xml
<parent>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<relativePath>....</relativePath>
</parent>
但因为原项目是一个springboot项目,而所有的springboot项目都有一个统一的父工程,就是spring-boot-starter-parent。 与java语言类似,Maven不支持多继承,一个maven项目只能继承一个父工程,如果继承了spring-bootstarter-parent,就没法继承我们自己定义的父工程 tlias-parent了。
Java虽然不支持多继承,但是可以支持多重继承,那在Maven中也是支持多重继承的,所以可以让我们自己创建的三个模块,都继承tlias-parent,而tlias-parent 再继承 spring-boot-starter-parent,就可以了:
- 创建maven模块 tlias-parent ,该工程为父工程,在pom里需要设置打包方式为pom(默认jar)
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima</groupId>
<artifactId>tlias-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
Maven打包方式:
jar:普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行),如我们上面的三个模块都是jar包打包
war:普通web程序打包,需要部署在外部的tomcat服务器中运行,现在用的很少
pom:用于父工程或聚合工程,该模块不写代码,仅进行依赖管理
工程结构如下:
因为tlias-parent是一个父工程,不会写任何代码,所以可以删除src文件。
- 在子工程的pom.xml文件中,配置继承关系。这里是以 tlias-utils 为例,指定了其父工程。其他的模块,都是相同的配置方式。
xml
<parent>
<groupId>com.itheima</groupId>
<artifactId>tlias-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../tlias-parent/pom.xml</relativePath>
</parent>
<artifactId>tlias-utils</artifactId>
<version>1.0-SNAPSHOT</version>
在子工程中,配置了继承关系之后,子工程的groupId是可以省略的,因为会自动继承父工程的 。
relativePath指定父工程的pom文件的相对位置(如果不指定,将从本地仓库/远程仓库查找该工程)。.../ 代表的上一级目录
- 在父工程中配置各个工程共有的依赖(子工程会自动继承父工程的依赖)。
xml
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
此时,我们已经将各个子工程中共有的依赖(lombok),都定义在了父工程中,子工程中的这一项依赖,就可以直接删除了。删除之后,我们会看到父工程中配置的依赖 lombok,子工程直接继承下来了。
PS:
我们当前的项目结构为:
实际项目中,可能还会见到下面的工程结构:
在真实的企业开发中,都是先设计好模块之后,再开始创建模块,开发项目。 那此时呢,一般都会先创建父工程 tlias-parent,然后将创建的各个子模块,都放在父工程parent下面。 这样层级结构会更加清晰一些。两种项目结构都可以,只不过第二种看起来更清晰直观。
PS:若父子工程都配置了同一个依赖的不同版本,则以子工程的为准。
版本锁定<dependencyManagement>
上面简化子工程当中的依赖配置只是maven继承的一个功能,实现了继承关系之后还可以在父工程中统一管理依赖版本,称之为版本锁定。
思考如下场景,某个依赖不是各个模块共有的,只是一部分模块需要,则需要在各个模块中单独配置,既然是同一个项目则需要各个模块中的依赖是同一个版本便于管理维护,将来需要升级这个依赖的版本也需要去各个模块中修改,则很有可能遗漏。
在maven中,可以在父工程的pom文件中通过<dependencyManagement>
来统一管理依赖版本。
在父工程中使用<dependencyManagement>
标签仅仅代表要统一管理这个依赖的版本,不会将这些依赖加入进来,子工程还需要自己引入这项依赖,只不过无需通过<version>
标签指定版本号了,只需在父工程中统一变更。
这便是版本锁定,但是还有一个问题,在父工程中配置版本号时,这个版本号是在各个依赖坐标当中配置的,那如果说将来我们配置的依赖呢比较多,这个里面将会配置大量的依赖,而这些版本号呢是非常零散地分布在各依赖坐标当中的。那此时还是不便于集中的管理和维护的。
而在maven中提供了自定义属性 的功能,通过自定义属性以及属性的引用就可以解决这个问题,比如我们可以在pom.xml中来通过properties来自定义属性:
属性名可以自定义,前提是起的有一定的业务含义的
聚合 <modules>
分模块设计与开发之后项目被拆分为多个模块,而模块之间的关系,可能错综复杂。 那就比如我们当前的案例项目,结构如下(相对还是比较简单的):
此时,tlias-web-management 模块的父工程是 tlias-parent,该模块又依赖了tlias-pojo、tlias-utils模块。 那此时,我们要想将 tlias-web-management 模块打包时,maven会从本地仓库中来查找其父工程tlias-parent,以及它所依赖的模块tlias-pojo、tlias-utils,而本地仓库目前是没有这几个依赖的。
所以,我们再打包tlias-web-management 模块前,需要将 tlias-parent、tlias-pojo、tlias-utils分别执行install生命周期安装到maven的本地仓库,然后再针对于 tlias-webmanagement模块执行package进行打包操作。如果开发一个大型项目,拆分的模块很多,模块之间的依赖关系错综复杂,那此时要进行项目的打包、安装操作,是非常繁琐的。 而maven的聚合就是来解决这个问题的
- 聚合:将多个模块组织成一个整体,同时进行项目的构建。
- 聚合工程:一个不具有业务功能的"空"工程(有且仅有一个pom文件) 【PS:一般来说,继承关系中的父工程与聚合关系中的聚合工程是同一个】
- 作用:快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)
在maven中,我们可以在聚合工程中通过<moudules>
设置当前聚合工程所包含的子模块的名称。我们可以在 tlias-parent中,添加如下配置,来指定当前聚合工程,需要聚合的模块:
xml
<!--聚合其他模块-->
<modules>
<module>../tlias-pojo</module>
<module>../tlias-utils</module>
<module>../tlias-web-management</module>
</modules>
那此时,我们要进行编译、打包、安装操作,就无需在每一个模块上操作了。只需要在聚合工程上,统一进行操作就可以了。在聚合工程 tlias-parent 中执行 package 打包指令:
tlias-parent 中所聚合的其他模块全部都会执行 package 指令,这就是通过聚合实现项目的一键构建(一键清理clean、一键编译compile、一键测试test、一键打包package、一键安装install等)。
继承与聚合对比八股:
-
作用
-
聚合用于快速构建项目
-
继承用于简化依赖配置、统一管理依赖
-
-
相同点:
-
聚合与继承的pom.xml文件打包方式均为pom,通常将两种关系制作到同一个pom文件中
-
聚合与继承均属于设计型模块,并无实际的模块内容
-
-
不同点:
-
聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些
-
继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
-
私服
前面讲到我们所拆分的模块是可以在同一个公司各个项目组之间进行资源共享的。这个模块的资源共享,就需要通过我们接下来所讲解的 Maven 的私服来实现。
假设现在有两个团队,A 和 B。 A 开发了一个模块 tlias-utils,模块开发完毕之后,将模块打成jar包,并安装到了A的本地仓库。那此时,该公司的B团队开发项目时,要想使用 tlias-utils 中提供的工具类,是不是在pom.xml文件中引入 tlias-utils的坐标就可以了呢?肯定不行
maven查找依赖的顺序为:1). 本地仓库,2). 远程中央仓库
因为目前tlias-utils这个依赖,还在A的本地仓库中的,B电脑上的maven项目,是不可能找得到A电脑上maven本地仓库的jar包的。而第二条途径也不行,中央仓库全球只有一个,我们是没有权限操作的。
那此时,maven的私服就出场了,私服其实就是架设在公司局域网内部的一台服务器,就是一种特殊的远程仓库。
私服:是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。
maven依赖查找顺序:1.本地仓库 2.私服仓库 3.中央仓库
私服在企业项目开发中,一个项目/公司,只需要一台即可(无需我们自己搭建,会使用即可)。
私服资源上传与下载
资源上传与下载,我们需要做三步配置:
第一步配置:在maven的配置文件中配置访问私服的用户名、密码。
第二步配置:在maven的配置文件中配置连接私服的地址(url地址)。
第三步配置:在项目的pom.xml文件中配置上传资源的位置(url地址)。
配置好了上述三步之后,要上传资源到私服仓库,就执行执行maven生命周期:deploy(发布)。
私服仓库说明:
RELEASE:存储自己开发的RELEASE发布版本的资源。
SNAPSHOT:存储自己开发的SNAPSHOT发布版本的资源。
Central:存储的是从中央仓库下载下来的依赖。
项目版本说明:RELEASE(发布版本):功能趋于稳定、当前更新停止,可以用于发行的版本,存储在私服中的RELEASE仓库中。
SNAPSHOT(快照版本):功能不稳定、尚处于开发中的版本,即快照版本,存储在私服的SNAPSHOT仓库中。
为了模拟企业开发,这里我准备好了一台服务器(192.168.150.101),私服准备好了之后,我们要做如下几步配置:
1.设置私服的访问用户名/密码(在自己maven安装目录下的conf/settings.xml中的servers中配置)
xml
<server>
<id>maven-releases</id>
<username>admin</username>
<password>admin</password>
</server>
<server>
<id>maven-snapshots</id>
<username>admin</username>
<password>admin</password>
</server>
2.设置私服依赖下载的仓库组地址(在自己maven安装目录下的conf/settings.xml中的mirrors、profiles中配置)
xml
<mirror>
<id>maven-public</id>
<mirrorOf>*</mirrorOf>
<url>http://192.168.150.101:8081/repository/maven-public/</url>
</mirror>
xml
<profile>
<id>allow-snapshots</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>maven-public</id>
<url>http://192.168.150.101:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
3.IDEA的maven工程的pom文件中配置上传(发布)地址(直接在tlias-parent中配置发布地址)
xml
<distributionManagement>
<!-- release版本的发布地址 -->
<repository>
<id>maven-releases</id>
<url>http://192.168.150.101:8081/repository/maven-releases/</url>
</repository>
<!-- snapshot版本的发布地址 -->
<snapshotRepository>
<id>maven-snapshots</id>
<url>http://192.168.150.101:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
配置完成之后,我们就可以在tlias-parent中执行deploy生命周期,将项目发布到私服仓库中。
通过日志,我们可以看到,这几个模块打的jar包确实已经上传到了私服仓库中(由于当前我们的项目<version>
后缀是SNAPSHOT版本,所以jar包是上传到了snapshot仓库中)。
那接下来,我们再来打开私服来看一下:
我们看到,我们项目中的这几个模块,在私服中都有了。 那接下来,当其他项目组的开发人员在项目中,就可以直接通过依赖的坐标,就可以完成引入对应的依赖,此时本地仓库没有,就会自动从私服仓库中下载。