maven
1.分模块开发与设计
分模块开发的意义
- 将原始模块按功能拆分成若干个子模块, 方便模块之间的相互调用, 接口共享
1.1项目的拆分的步骤
(1) 创建Maven模块
(2) 书写模块代码
分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分。拆分方式可以按照功能拆也可以按照模块拆。
(3)通过maven指令安装模块到本地仓库(install 指令)
团队内部开发需要发布模块功能到团队内部可共享的仓库中(私服)
2.依赖管理
1.依赖传递
直接依赖: 在当前项目中通过依赖配置建立的依赖关系
间接依赖: 被依赖的资源依赖的其他资源—>依赖的依赖
2.依赖冲突
依赖冲突问题
- 路径优先: 当依赖中出现相同的资源时, 层级越深, 优先级越低, 层级越浅, 优先级越高
- 声明优先: 当资源在相同层级被依赖时, 配置顺序靠前的覆盖配置顺序靠后的
- 特殊优先: 当同级资源配置了相同资源的不同版本, 后配置覆盖先配置的(pom.xml中dependency顺序)
依赖传递。
说明:A代表自己的项目;B,C,D,E,F,G代表的是项目所依赖的jar包;D1和D2 E1和E2代表是相同jar包的不同版本
(1) A依赖了B和C,B和C有分别依赖了其他jar包,所以在A项目中就可以使用上面所有jar包,这就是所说的依赖传递
(2) 依赖传递有直接依赖和间接依赖
- 相对于A来说,A直接依赖B和C,间接依赖了D1,E1,G,F,D2和E2
- 相对于B来说,B直接依赖了D1和E1,间接依赖了G
- 直接依赖和间接依赖是一个相对的概念
(3)因为有依赖传递的存在,就会导致jar包在依赖的过程中出现冲突问题,具体什么是冲突?Maven是如何解决冲突的?
这里所说的==依赖冲突==是指项目依赖的某一个jar包,有多个不同的版本,因而造成类包版本冲突。
情况一: 在maven_02_ssm
的pom.xml中添加两个不同版本的Junit依赖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
通过对比,会发现一个结论
- 特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的。
情况二: 路径优先:当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高
- A通过B间接依赖到E1
- A通过C间接依赖到E2
- A就会间接依赖到E1和E2,Maven会按照层级来选择,E1是2度,E2是3度,所以最终会选择E1
情况三: 声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的
- A通过B间接依赖到D1
- A通过C间接依赖到D2
- D1和D2都是两度,这个时候就不能按照层级来选择,需要按照声明来,谁先声明用谁,也就是说B在C之前声明,这个时候使用的是D1,反之则为D2
但是对应上面这些结果,大家不需要刻意去记它。因为不管Maven怎么选,最终的结果都会在Maven的Dependencies
面板中展示出来,展示的是哪个版本,也就是说它选择的就是哪个版本,如:
如果想更全面的查看Maven中各个坐标的依赖关系,可以点击Maven面板中的show Dependencies
在这个视图中就能很明显的展示出jar包之间的相互依赖关系。
3.可选依赖和排除依赖
依赖传递介绍完以后,我们来思考一个问题,
- maven_02_ssm 依赖了 maven_04_dao
- maven_04_dao 依赖了 maven_03_pojo
- 因为现在有依赖传递,所以maven_02_ssm能够使用到maven_03_pojo的内容
- 如果说现在不想让maven_02_ssm依赖到maven_03_pojo,有哪些解决方案?
方案一:可选依赖
- 可选依赖指对外隐藏当前所依赖的资源—不透明
在maven_04_dao
的pom.xml,在引入maven_03_pojo
的时候,添加optional
1
2
3
4
5
6
7
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_03_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
<!--可选依赖是隐藏当前工程所依赖的资源,隐藏后对应资源将不具有依赖传递-->
<optional>true</optional>
</dependency>
此时BookServiceImpl就已经报错了,说明由于maven_04_dao将maven_03_pojo设置成可选依赖,导致maven_02_ssm无法引用到maven_03_pojo中的内容,导致Book类找不到。
方案二:排除依赖
- 排除依赖指主动断开依赖的资源,被排除的资源无需指定版本—不需要
前面我们已经通过可选依赖实现了阻断maven_03_pojo的依赖传递,对于排除依赖,则指的是已经有依赖的事实,也就是说maven_02_ssm项目中已经通过依赖传递用到了maven_03_pojo,此时我们需要做的是将其进行排除,所以接下来需要修改maven_02_ssm的pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_04_dao</artifactId>
<version>1.0-SNAPSHOT</version>
<!--排除依赖是隐藏当前资源对应的依赖关系-->
<exclusions>
<exclusion>
<groupId>com.itheima</groupId>
<artifactId>maven_03_pojo</artifactId>
</exclusion>
</exclusions>
</dependency>
这样操作后,BookServiceImpl中的Book类一样也会报错。
当然exclusions
标签带s
说明我们是可以依次排除多个依赖到的jar包,比如maven_04_dao中有依赖junit和mybatis,我们也可以一并将其排除。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_04_dao</artifactId>
<version>1.0-SNAPSHOT</version>
<!--排除依赖是隐藏当前资源对应的依赖关系-->
<exclusions>
<exclusion>
<groupId>com.itheima</groupId>
<artifactId>maven_03_pojo</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
介绍我这两种方式后,简单来梳理下,就是
A依赖B,B依赖C
,C
通过依赖传递会被A
使用到,现在要想办法让A
不去依赖C
- 可选依赖是在B上设置
<optional>
,A
不知道有C
的存在, - 排除依赖是在A上设置
<exclusions>
,A
知道有C
的存在,主动将其排除掉。
4.继承与聚合
4.1聚合
聚合: 将多个模块组织成一个整体, 同时进行项目构建的过程称为聚合
聚合工程: 通常是一个不具有业务功能的空工程(仅有一个pom文件, 用来管理子模块的版本号, 每次版本升级都需要更新parent的版本号)
作用: 使用聚合工程可以将多个工程编组, 通过对聚合工程进行构建, 实现对所包含的模块进行通过构建
- 当工程中某个模块发生更新(变更时), 必须保障工程中与已更新模块关联的模块同步更新, 此时可以使用聚合工程来解决批量模块同步构建的问题
聚合工程开发
-
创建maven工程, 设置打包类型为pom
1
<packaging>pom</packaging>
每个maven工程都有对应的打包方式, 默认为jar, web工程的打包方式为war包
2.设置当前聚合工程所包含的子模块名称
1
2
3
4
5
<modules>
<module>../../maven_02_ssm/maven_02_ssm</module>
<module>../../maven_03_pojo</module>
<module>../../maven_04_dao/maven_04_dao</module>
</modules>
聚合工程中所包含的模块在进行构建时会根据模块间的依赖关系设置构建顺序, 与聚合工程中模块的配置书写位置无关, 参与聚合的工程无法向上感知是否参与聚合, 只能向下配置哪些模块参与了本工程的聚合
4.2继承
简化配置文件, 抽取共同的配置统一管理, 这个统一的配置就是父工程
概念: 继承描述的两个工程的关系, 与java中的继承相似, 子工程可以继承父工程中的配置信息, 常见于依赖关系的继承
作用:
- 简化配置, 抽取重复的, 统一管理配置版本号
- 简化版本冲突
4.3 继承quickstart
-
创建maven模块, 设置打包类型为pom
1
<packaging>pom</packaging>
-
在父工程中配置依赖关系(子工程将沿用工程中的依赖关系)
1 2 3 4 5 6 7 8
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> ...... <dependencies>
-
配置子工程中可选依赖关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!--定义可选依赖--> <dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.example</groupId> <artifactId>maven_03_pojo</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </dependencyManagement>
-
在子工程中配置当前工程中所继承的父工程
1 2 3 4 5 6
<parent> <groupId>org.example</groupId> <artifactId>maven_01_parent</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../maven_01_parent/pom.xml</relativePath> </parent>
-
在子工程中配置使用父工程中可选依赖的坐标
1 2 3 4 5 6
<dependencies> <dependency> <groupId>org.example</groupId> <artifactId>maven_03_pojo</artifactId> </dependency> </dependencies>
不用配置版本号
4.4聚合和继承的区别
- 作用
- 聚合用于快速构建项目 聚合工程主要是用来管理项目
- 继承用于快速配置
- 相同点
- 聚合与继承的pom.xml文件打包方式均为pom, 可以将两种关系制作到同一个pom中
- 聚合与继承均属于设计型模块, 并无实际的模块内容
- 不同点
- 聚合是在当前模块中配置关系, 聚合可以感知到参与的模块有哪些
- 继承是在子模块中配置关系, 父模块无法感知哪些子模块继承了自己
5.属性
5.1属性配置与使用
-
定义属性
1 2 3 4
<properties> <spring.version>5.2.10.RELEASE</spring.version> <junit.version>4.12</junit.version> </properties>
-
引用属性
1 2 3 4 5 6 7 8 9 10 11 12
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
5.2资源文件引用属性
1.父工程定义属性
1
2
3
4
5
<properties>
<spring.version>5.2.10.RELEASE</spring.version>
<junit.version>4.12</junit.version>
<jdbc.url>jdbc:mysql://localhost:3306/spring_db</jdbc.url>
</properties>
2.jdbc.properties文件中引用属性(子工程)
1
2
3
4
jdbc.driver-class-name=com.mysql.cj.jdbc.Driver
jdbc.url=${jdbc.url}
jdbc.username=root
jdbc.password=root
3.开启资源文件目录加载属性的过滤器(父工程)(关键, 解析: ${jdbc.url})
1
2
3
4
5
6
7
8
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
4.配置maven打war包时, 忽略web.xml检查(war包子工程)
1
2
3
4
5
6
7
8
9
10
11
12
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
上面的属性管理就已经完成,但是有一个问题没有解决,因为不只是maven_02_ssm项目需要有属性被父工程管理,如果有多个项目需要配置,该如何实现呢?
方式一:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<build>
<resources>
<!--设置资源目录,并设置能够解析${}-->
<resource>
<directory>../maven_02_ssm/src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>../maven_03_pojo/src/main/resources</directory>
<filtering>true</filtering>
</resource>
...
</resources>
</build>
可以配,但是如果项目够多的话,这个配置也是比较繁琐
方式二:
1
2
3
4
5
6
7
8
9
10
11
12
<build>
<resources>
<!--
${project.basedir}: 当前项目所在目录,子项目继承了父项目,
相当于所有的子项目都添加了资源目录的过滤
-->
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
5.3maven内置属性
上面我们所使用的都是Maven的自定义属性,除了${project.basedir},它属于Maven的内置系统属性。
在Maven中的属性分为:
- 自定义属性(常用)
- 内置属性
- Setting属性
- Java系统属性
- 环境变量属性
具体如何查看这些属性:
在cmd命令行中输入mvn help:system
具体使用,就是使用 ${key}
来获取,key为等号左边的,值为等号右边的,比如获取红线的值,对应的写法为 ${java.runtime.name}
。
5.3版本管理
jar包的版本定义中,有两个工程版本用的比较多:
- SNAPSHOT(快照版本)
- 项目开发过程中临时输出的版本,称为快照版本
- 快照版本会随着开发的进展不断更新
- RELEASE(发布版本)
- 项目开发到一定阶段里程碑后,向团队外部发布较为稳定的版本,这种版本所对应的构件文件是稳定的
- 即便进行功能的后续开发,也不会改变当前发布版本内容,这种版本称为发布版本
除了上面的工程版本,我们还经常能看到一些发布版本:
- alpha版:内测版,bug多不稳定内部版本不断添加新功能
- beta版:公测版,不稳定(比alpha稳定些),bug相对较多不断添加新功能
- 纯数字版
对于这些版本,大家只需要简单认识下即可。
6.多环境配置与应用
6.1 多环境开发
- maven提供配置多种环境的设定, 帮助开发者使用过程中快速切换环境
比如各种环境的数据库不一致, 需要切换数据库
quickstart
-
定义多环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
<!--配置多环境--> <profiles> <!--开发环境--> <profile> <id>env_dev</id> <properties> <jdbc.url>jdbc:mysql://127.0.0.1:3306/spring_db</jdbc.url> </properties> <!--设置为默认环境--> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <!--测试环境--> <profile> <id>env_test</id> <properties> <jdbc.url>jdbc:mysql://test/spring_db</jdbc.url> </properties> </profile> <!--生成环境--> <profile> <id>env_prod</id> <properties> <jdbc.url>jdbc:mysql://prod/spring_db</jdbc.url> </properties> </profile> </profiles>
-
使用多环境(构建)
mvn 指令 -P 环境id
范例:(指令优先级高于activeByDefault标签定义的默认优先级)
1
mvn install -P env_prod
6.2跳过测试
- 应用场景
- 功能更新中但功能没有开发完毕
- 快速打包
- …
quickstart
跳过测试指令
-
1
mvn 指令 -D skipTests
-
1
mvn package -D skipTests
执行的命令生命周期包含test配置该功能才有效, compile就没有test阶段
细粒度控制跳过测试
1
2
3
4
5
6
7
8
9
10
11
12
13
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>false</skipTests>
<!--包含参与测试的内容-->
<includes>**/User*Test.java</includes>
<!--排除不参与测试的内容-->
<excludes>**/BookService*Test.java</excludes>
</configuration>
</plugin>
</plugins>
7.私服
7.1私服简介
- 私服是一台独立的服务器, 用于解决团队内部的资源共享与资源同步问题
- Nexus
- sonatype公司的一款maven私服产品
- 下载地址
7.2 私服安装
进入到nexus的bin目录下
执行命令
nexus.exe /run nexus
访问地址
1
http://localhost:8081
至此私服就已经安装成功。如果要想修改一些基础配置信息,可以使用:
- 修改基础配置信息
- 安装路径下etc目录中nexus-default.properties文件保存有nexus基础配置信息,例如默认访问端口。
- 修改服务器运行配置信息
- 安装路径下bin目录中nexus.vmoptions文件保存有nexus服务器启动对应的配置信息,例如默认占用内存空间
7.3私服仓库分类
(1)在没有私服的情况下,我们自己创建的服务都是安装在Maven的本地仓库中
(2)私服中也有仓库,我们要把自己的资源上传到私服,最终也是放在私服的仓库中
(3)其他人要想使用你所上传的资源,就需要从私服的仓库中获取
(4)当我们要使用的资源不是自己写的,是远程中央仓库有的第三方jar包,这个时候就需要从远程中央仓库下载,每个开发者都去远程中央仓库下速度比较慢(中央仓库服务器在国外)
(5)私服就再准备一个仓库,用来专门存储从远程中央仓库下载的第三方jar包,第一次访问没有就会去远程中央仓库下载,下次再访问就直接走私服下载
(6)前面在介绍版本管理的时候提到过有SNAPSHOT
和RELEASE
,如果把这两类的都放到同一个仓库,比较混乱,所以私服就把这两个种jar包放入不同的仓库
(7)上面我们已经介绍了有三种仓库,一种是存放SNAPSHOT
的,一种是存放RELEASE
还有一种是存放从远程仓库下载的第三方jar包,那么我们在获取资源的时候要从哪个仓库种获取呢?
(8)为了方便获取,我们将所有的仓库编成一个组,我们只需要访问仓库组去获取资源。
所有私服仓库总共分为三大类:
宿主仓库hosted
- 保存无法从中央仓库获取的资源
- 自主研发
- 第三方非开源项目,比如Oracle,因为是付费产品,所以中央仓库没有
代理仓库proxy
- 代理远程仓库,通过nexus访问其他公共仓库,例如中央仓库
仓库组group
- 将若干个仓库组成一个群组,简化配置
- 仓库组不能保存资源,属于设计型仓库
7.4 本地仓库访问私服配置
- 我们通过IDEA将开发的模块上传到私服,中间是要经过本地Maven的
- 本地Maven需要知道私服的访问地址以及私服访问的用户名和密码
- 私服中的仓库很多,Maven最终要把资源上传到哪个仓库?
- Maven下载的时候,又需要携带用户名和密码到私服上找对应的仓库组进行下载,然后再给IDEA
上面所说的这些内容,我们需要在本地Maven的配置文件settings.xml
中进行配置。
步骤1:私服上配置仓库
说明:
第5,6步骤是创建itheima-snapshot仓库
第7,8步骤是创建itheima-release仓库
步骤2:配置本地Maven对私服的访问权限
1
2
3
4
5
6
7
8
9
10
11
<!--配置访问私服的权限-->
<server>
<id>ssm-Release</id>
<username>admin</username>
<password>admin</password>
</server>
<server>
<id>ssm-Snapshot</id>
<username>admin</username>
<password>admin</password>
</server>
步骤3:配置私服的访问路径
1
2
3
4
5
6
7
8
9
10
11
<mirrors>
<mirror>
<!--配置仓库组的ID-->
<id>maven-public</id>
<!--*代表所有内容都从私服获取-->
<mirrorOf>*</mirrorOf>
<!--私服仓库组maven-public的访问路径-->
<url>http://localhost:8081/repository/maven-public/</url>
<name>Local Host Nexus</name>
</mirror>
</mirrors>
为了避免阿里云Maven私服地址的影响,建议先将之前配置的阿里云Maven私服镜像地址注释掉,等练习完后,再将其恢复。
至此本地仓库就能与私服进行交互了。
7.5 私服资源上传与下载
本地仓库与私服已经建立了连接,接下来我们就需要往私服上上传资源和下载资源,具体的实现步骤为:
步骤1:配置工程上传私服的具体位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--配置当前工程保存在私服中的具体位置-->
<distributionManagement>
<repository>
<!--和maven/settings.xml中server中的id一致,表示使用该id对应的用户名和密码-->
<id>ssm-Release</id>
<!--release版本上传仓库的具体地址-->
<url>http://localhost:8081/repository/ssm-Release/</url>
</repository>
<snapshotRepository>
<!--和maven/settings.xml中server中的id一致,表示使用该id对应的用户名和密码-->
<id>ssm-Snapshot</id>
<!--snapshot版本上传仓库的具体地址-->
<url>http://localhost:8081/repository/ssm-Snapshot/</url>
</snapshotRepository>
</distributionManagement>
步骤2:发布资源到私服
或者执行Maven命令
1
mvn deploy
注意:
要发布的项目都需要配置distributionManagement
标签,要么在自己的pom.xml中配置,要么在其父项目中配置,然后子项目中继承父项目即可。
发布成功,在私服中就能看到:
现在发布是在itheima-snapshot仓库中,如果想发布到itheima-release仓库中就需要将项目pom.xml中的version修改成RELEASE即可。
如果想删除已经上传的资源,可以在界面上进行删除操作:
如果私服中没有对应的jar,会去中央仓库下载,速度很慢。可以配置让私服去阿里云中下载依赖。
至此私服的搭建就已经完成