项目中jar包资源越来越多,jar包的管理越来越沉重。
要为每个项目手动导入所需的jar,需要搜集全部jar
项目中的jar如果需要版本升级,就需要再重新搜集jar
相同的jar在不同的项目中保存了多份
在今天的JavaEE开发领域,有大量的第三方框架和工具可以供我们使用。要使用这些jar包最简单的方法就是复制粘贴到WEB-INF目录下的lib目录下。但是这会导致每次创建一个新的工程就需要将jar包重复复制到lib目录下,从而造成工作区中存在大量重复的文件。
而使用Maven后每个jar包只在本地仓库中保存一份,需要jar包的工程只需要维护一个文本形式的jar包的引用——我们称之为“坐标”。不仅极大的节约了存储空间,让项目更轻巧,更避免了重复文件太多而造成的混乱。
jar包往往不是孤立存在的,很多jar包都需要在其他jar包的支持下才能够正常工作,我们称之为jar包之间的依赖关系。最典型的例子是:commons-fileupload-1.3.jar依赖于commons-io-2.0.1.jar,如果没有IO包,FileUpload包就不能正常工作。
那么问题来了,你知道你所使用的所有jar包的依赖关系吗?当你拿到一个新的从未使用过的jar包,你如何得知他需要哪些jar包的支持呢?如果不了解这个情况,导入的jar包不够,那么现有的程序将不能正常工作。再进一步,当你的项目中需要用到上百个jar包时,你还会人为的,手工的逐一确认它们依赖的其他jar包吗?这简直是不可想象的。
而引入Maven后,Maven就可以替我们自动的将当前jar包所依赖的其他所有jar包全部导入进来,无需人工参与,节约了我们大量的时间和精力。用实际例子来说明就是:通过Maven导入commons-fileupload-1.3.jar后,commons-io-2.0.1.jar会被自动导入,程序员不必了解这个依赖关系。
上一点说的是jar包不足项目无法正常工作,但其实有的时候jar包多了项目仍然无法正常工作,这就是jar包之间的冲突。
举个例子:我们现在有三个工程MakeFriend、HelloFriend、和Hello。MakeFriend依赖HelloFriend,HelloFriend依赖Hello。而Hello依赖log4j.1.2.17.jar,HelloFriend依赖log4j.1.2.14.jar。如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V51DbKOl-1692863772990)(http://img.itboyhub.com//2020/04/vhr/20210327080831.png)]
那么MakeFriend工程的运行时环境中该导入log4j.1.2.14.jar呢还是log4j.1.2.17.jar呢?
这样的问题一个两个还可以手工解决,但如果系统中存在几十上百的jar包,他们之间的依赖关系会非常复杂,几乎不可能手工实现依赖关系的梳理。
使用Maven就可以自动的处理jar包之间的冲突问题。因为Maven中内置了两条依赖原则:最短路径者优先和先声明者优先,上述问题MakeFriend工程会自动使用log4j.1.2.14.jar。
JavaEE开发中需要使用到的jar包种类繁多,几乎每个jar包在其本身的官网上的获取方式都不尽相同。为了查找一个jar包找遍互联网,身心俱疲,没有经历过的人或许体会不到这种折磨。不仅如此,费劲心血找的jar包里有的时候并没有你需要的那个类,又或者又同名的类没有你要的方法——以不规范的方式获取的jar包也往往是不规范的。
使用Maven我们可以享受到一个完全统一规范的jar包管理体系。你只需要在你的项目中以坐标的方式依赖一个jar包,Maven就会自动从中央仓库进行下载,并同时下载这个jar包所依赖的其他jar包——规范、完整、准确!一次性解决所有问题!
Tips:在这里我们顺便说一下,统一的规范几乎可以说成是程序员的最高信仰。如果没有统一的规范,就意味着每个具体的技术都各自为政,需要以诸多不同的特殊的方式加入到项目中;好不容易加入进来还会和其他技术格格不入,最终受苦的是我们。而任何一个领域的统一规范都能够极大的降低程序员的工作难度,减少工作量。例如:USB接口可以外接各种设备,如果每个设备都有自己独特的接口,那么不仅制造商需要维护各个接口的设计方案,使用者也需要详细了解每个设备对应的接口,无疑是非常繁琐的。
随着JavaEE项目的规模越来越庞大,开发团队的规模也与日俱增。一个项目上千人的团队持续开发很多年对于JavaEE项目来说再正常不过。那么我们想象一下:几百上千的人开发的项目是同一个Web工程。那么架构师、项目经理该如何划分项目的模块、如何分工呢?这么大的项目已经不可能通过package结构来划分模块,必须将项目拆分成多个工程协同开发。多个模块工程中有的是Java工程,有的是Web工程。
那么工程拆分后又如何进行互相调用和访问呢?这就需要用到Maven的依赖管理机制。大家请看我们的Survey调查项目拆分的情况:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QgY1PeXK-1692863772990)(http://img.itboyhub.com//2020/04/vhr/20210327080903.png)]
上层模块依赖下层,所以下层模块中定义的API都可以为上层所调用和访问。
在实际生产环境中,项目规模增加到一定程度后,可能每个模块都需要运行在独立的服务器上,我们称之为分布式部署,这里同样需要用到Maven。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FLts6Js5-1692863772990)(http://img.itboyhub.com//2020/04/vhr/20210327080933.png)]
Ant
Maven
Gradle
Maven这个单词来自于意第绪语(犹太语),意为知识的积累.
Maven是一个基于项目对象模型(POM)的概念的纯java开发的开源的项目管理工具。主要用来管理java项目,进行依赖管理(jar包依赖管理)和项目构建(项目编译、打包、测试、部署)。此外还能分模块开发,提高开发效率。
Maven这个单词的本意是:专家,内行。读音是['meɪv(ə)n]或['mevn],不要读作“妈文”。
Maven是一款自动化构建工具,专注服务于Java平台的项目构建和依赖管理。在JavaEE开发的历史上构建工具的发展也经历了一系列的演化和变迁:
Make→Ant→Maven→Gradle→其他……
那么什么是构建呢?
构建并不是创建,创建一个工程并不等于构建一个项目。要了解构建的含义我们应该由浅入深的从以下三个层面来看:
①纯Java代码
大家都知道,我们Java是一门编译型语言,.java扩展名的源文件需要编译成.class扩展名的字节码文件才能够执行。所以编写任何Java代码想要执行的话就必须经过编译得到对应的.class文件。
②Web工程
当我们需要通过浏览器访问Java程序时就必须将包含Java程序的Web工程编译的结果“拿”到服务器上的指定目录下,并启动服务器才行。这个“拿”的过程我们叫部署。
我们可以将未编译的Web工程比喻为一只生的鸡,编译好的Web工程是一只煮熟的鸡,编译部署的过程就是将鸡炖熟。
③实际项目
在实际项目中整合第三方框架,Web工程中除了Java程序和JSP页面、图片等静态资源之外,还包括第三方框架的jar包以及各种各样的配置文件。所有这些资源都必须按照正确的目录结构部署到服务器上,项目才可以运行。
所以综上所述:构建就是以我们编写的Java代码、框架配置文件、国际化等其他资源文件、JSP页面和图片等静态资源作为“原材料”,去“生产”出一个可以运行的项目的过程。
那么项目构建的全过程中都包含哪些环节呢?
①清理:删除以前的编译结果,为重新编译做好准备。
②编译:将Java源程序编译为字节码文件。
③测试:针对项目中的关键点进行测试,确保项目在迭代开发过程中关键点的正确性。
④报告:在每一次测试后以标准的格式记录和展示测试结果。
⑤打包:将一个包含诸多文件的工程封装为一个压缩文件用于安装或部署。Java工程对应jar包,Web工程对应war包。
⑥安装:在Maven环境下特指将打包的结果——jar包或war包安装到本地仓库中。
⑦部署:将打包的结果部署到远程仓库或将war包部署到服务器上运行。
Maven之所以能够实现自动化的构建,和它的设计是紧密相关的。我们对Maven的学习就围绕它的九个核心概念展开:
①POM
②约定的目录结构
③坐标
④依赖管理
⑤仓库管理
⑥生命周期
⑦插件和目标
⑧继承
⑨聚合
下载Maven
http://us.mirrors.quenda.co/apache/maven/maven-3/3.5.4/binaries/ |
---|
|
注意: 解压文件尽量不要放在含有中文或者特殊字符的目录下。
解压后,有如下目录:
`bin:含有mvn运行的脚本`
`boot:含有plexus-classworlds类加载器框架,Maven 使用该框架加载自己的类库。`
`conf:含有settings.xml配置文件`
`lib:含有Maven运行时所需要的java类库`
maven依赖java环境,所以要确保java环境已配置好 (maven-3.3+ 需要jdk7+)
maven本身有2个环境变量要配置:
`MAVEN_HOME = maven的安装目录`
`PATH = maven的安装目录下的bin目录`
查看maven版本信息
mvn -v
maven的conf目录中有 settings.xml ,是maven的配置文件,做如下配置:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:\Program Files\maven\myrepositorylocalRepository>
在 标签中 增加 一个 标签,限定maven项目默认的jdk版本.
内容如下:
<profiles>
<profile>
<id>myjdkid>
<activation>
<activeByDefault>trueactiveByDefault>
<jdk>1.8jdk>
activation>
<properties>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<maven.compiler.compilerVersion>1.8maven.compiler.compilerVersion>
properties>
profile>
profiles>
<activeProfiles>
<activeProfile>myjdkactiveProfile>
activeProfiles>
存储依赖的地方,体现形式就是本地的一个目录。
仓库中不仅存放依赖,而且管理着每个依赖的唯一标识(坐标),Java项目凭坐标获取依赖。
仓库分类如下:
仓库分类 |
---|
当需要依赖时,会从仓库中取查找,优先顺序为:
本地仓库 > 私服(如果配置了的话) > 公共仓库(如果配置了的话) > 中央仓库
即在settings.xml 中配置的目录。
使用过了的依赖都会自动存储在本地仓库中,后续可以复用。
Maven 中央仓库是由 Maven 社区提供的仓库,不用任何配置,maven中内置了中央仓库的地址。
其中包含了绝大多数流行的开源Java构件。
https://mvnrepository.com/ 可以搜索需要的依赖的相关信息(仓库搜索服务)
http://repo.maven.apache.org/maven2/ 中央仓库地址
重点
】
除中央仓库之外,还有其他远程仓库。
比如aliyun仓库(http://maven.aliyun.com/nexus/content/groups/public/)中央仓库在国外,下载依赖速度过慢,所以都会配置一个国内的公共仓库替代中央仓库。
<mirrors>
<mirror>
<id>aliyunid>
<mirrorOf>centralmirrorOf>
<name>Nexus aliyunname>
<url>http://maven.aliyun.com/nexus/content/groups/publicurl>
mirror>
mirrors>
公司范围内共享的仓库,不对外开放。
可以通过 Nexus来创建、管理一个私服。
在idea中关联本地安装的maven,后续就可以通过idea使用maven,管理项目啦。
在全局设置中,关联Maven |
---|
①路径最短者优先
②路径相同时先声明者优先
这里“声明”的先后顺序指的是dependency标签配置的先后顺序。
有的时候为了确保程序正确可以将有可能重复的间接依赖排除。请看如下的例子:
<dependency>
<groupId>org.javaboy.mavengroupId>
<artifactId>Survey160225_4_EnvironmentartifactId>
<version>0.0.1-SNAPSHOTversion>
<exclusions>
<exclusion>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
<version>1.1.2version>
dependency>
```
### 8.7 统一管理目标jar包的版本
以对Spring的jar包依赖为例:Spring的每一个版本中都包含spring-core、spring-context等jar包。我们应该导入版本一致的Spring jar包,而不是使用4.0.0的spring-core的同时使用4.1.1的spring-context。
```xml
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>4.0.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.0.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>4.0.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>4.0.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>4.0.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>4.0.0.RELEASEversion>
dependency>
问题是如果我们想要将这些jar包的版本统一升级为4.1.1,是不是要手动一个个修改呢?显然,我们有统一配置的方式:
<properties>
<spring.version>4.1.1.RELEASEspring.version>
properties>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
这样一来,进行版本调整的时候只改一改地方就行了。
Maven生命周期定义了各个构建环节的执行顺序,有了这个清单,Maven就可以自动化的执行构建命令了。
Maven有三套相互独立的生命周期,分别是:
再次强调一下它们是相互独立的,你可以仅仅调用clean来清理工作目录,仅仅调用site来生成站点。当然你也可以直接运行 mvn clean install site 运行所有这三套生命周期。
每套生命周期都由一组阶段(Phase)组成,我们平时在命令行输入的命令总会对应于一个特定的阶段。比如,运行mvn clean,这个clean是Clean生命周期的一个阶段。有Clean生命周期,也有clean阶段。
Clean生命周期一共包含了三个阶段:
这里经常用到的是site阶段和site-deploy阶段,用以生成和发布Maven站点,这可是Maven相当强大的功能,Manager比较喜欢,文档及统计数据自动生成,很好看。
Default生命周期是Maven生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。这里,只解释一些比较重要和常用的阶段:
运行任何一个阶段的时候,它前面的所有阶段都会被运行,例如我们运行mvn install 的时候,代码会被编译,测试,打包。这就是Maven为什么能够自动执行构建过程的各个环节的原因。此外,Maven的插件机制是完全依赖Maven的生命周期的,因此理解生命周期至关重要。
由于非compile范围的依赖信息是不能在“依赖链”中传递的,所以有需要的工程只能单独配置。例如:
Hello
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.9version>
<scope>testscope>
dependency>
HelloFriend
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.9version>
<scope>testscope>
dependency>
MakeFriend
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.9version>
<scope>testscope>
dependency>
此时如果项目需要将各个模块的junit版本统一为4.9,那么到各个工程中手动修改无疑是非常不可取的。使用继承机制就可以将这样的依赖信息统一提取到父工程模块中进行统一管理。
创建父工程和创建一般的Java工程操作一致,唯一需要注意的是:打包方式处要设置为pom。
<parent>
<groupId>...groupId>
<artifactId>...artifactId>
<version>...version>
<relativePath>从当前目录到父项目的pom.xml文件的相对路径relativePath>
parent>
此时如果子工程的groupId和version如果和父工程重复则可以删除。
将Parent项目中的dependencies标签,用dependencyManagement标签括起来
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.9version>
<scope>testscope>
dependency>
dependencies>
dependencyManagement>
在子项目中重新指定需要的依赖,删除范围和版本号
junit
junit
将多个工程拆分为模块后,需要手动逐个安装到仓库后依赖才能够生效。修改源码后也需要逐个手动进行clean操作。而使用了聚合之后就可以批量进行Maven工程的安装、清理工作。
在总的聚合工程中使用modules/module标签组合,指定模块工程的相对路径即可
<modules>
<module>../Hellomodule>
<module>../HelloFriendmodule>
<module>../MakeFriendsmodule>
modules>
私服是架设在局域网的一种特殊的远程仓库,目的是代理远程仓库及部署第三方构件。
有了私服之后,当 Maven 需要下载依赖时,直接请求私服,私服上存在则下载到本地仓库;否则,私服请求外部的远程仓库,将构件下载到私服,再提供给本地仓库下载。
私服可以解决在企业做开发时每次需要的jar包都要在中心仓库下载,且每次下载完只能被自己使用,不能被其他开发人员使用
所谓私服就是一个服务器,但是不是本地层面的,是公司层面的,公司中所有的开发人员都在使用同一个私服
无私服 | 有私服 |
---|---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dlOPBR6m-1692863773000)(Pictures/私服1.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flOxQw1Q-1692863773000)(Pictures/私服2.png)] |
我们可以使用专门的 Maven 仓库管理软件来搭建私服,比如:Apache Archiva,Artifactory,Sonatype Nexus。这里我们使用 Sonatype Nexus
- 官网:https://blog.sonatype.com/
- 下载地址:https://help.sonatype.com/repomanager2/download/download-archives---repository-manager-oss
下载nexus-2.x-bundle.zip,解压即可
- 解压后在bin目录中执行:
- nexus install 在系统中安装nexus服务
- nexus uninstall 卸载nexus服务
- nexus start 启动服务
- nexus stop 停止服务
访问私服:http://localhost:8081/nexus/
登录Nexus才可以使用Nexus管理功能 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mZTvO38O-1692863773001)(Pictures/私服_login.jpg)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZyXD3gkJ-1692863773001)(Pictures/私服_login2.jpg)] |
仓库类型 | 描述 |
---|---|
group | 包含多个仓库,通过group库的地址可以从包含的多个仓库中查找构件 |
hosted | 私服 服务器本地的仓库,其中存储诸多构件 |
proxy | 代理仓库,其会关联一个远程仓库, 比如中央仓库,aliyun仓库,向该仓库查找构件时,如果没有会从其关联的仓库中下载 |
仓库名 | 描述 |
---|---|
Releases | 存放项目的稳定发布版本,一个模块做完后如果需要共享给他人,可以上传到私服的该库 |
Snapshots | 对应不稳定的发布版本 |
3rd party | 存放中央仓库没有的 ,如ojdbc.jar,可以上传到私服的该库中 |
仓库列表 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FTxfKVG-1692863773001)(Pictures/私服_list.jpg)] |
重点
】在maven中配置私服,使得maven可以从私服上获取构件
而此时就有问题,私服中有很多仓库,每个仓库都有自己的url,则项目中配置哪个仓库呢 ?
私服中有一个仓库组,组中包含多个仓库,可以指定仓库组的url,即可从多个仓库中获取构件
| 仓库组 注意:proxy的仓库排序在最后 |
配置settings.xml,设置私服地址、认证等信息
<servers>
<server>
<id>nexus-publicid>
<username>adminusername>
<password>admin123password>
server>
servers>
<profiles>
<profile>
<id>nexusid>
<repositories>
<repository>
<id>nexus-publicid>
<name>Nexus Release Snapshot Repositoryname>
<url>http://localhost:8081/nexus/content/groups/public/url>
<releases><enabled>trueenabled>releases>
<snapshots><enabled>trueenabled>snapshots>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus-publicid>
<url>http://localhost:8081/nexus/content/groups/public/url>
<releases><enabled>trueenabled>releases>
<snapshots><enabled>trueenabled>snapshots>
pluginRepository>
pluginRepositories>
profile>
profiles>
<activeProfiles>
<activeProfile>yourjdkactiveProfile>
<activeProfile>nexusactiveProfile>
activeProfiles>
至此,Maven项目中需要依赖时,Maven会从私服中下载
执行 :mvn deploy 即可将项目部署到私服对应的仓库中,此时项目中的打包方式多为jar
但需要提前在项目的pom.xml中配置部署私服仓库位置,如下:
...
<dependencies>
.....
dependencies>
<distributionManagement>
<repository>
<id>nexus-publicid>
<url>http://localhost:8081/nexus/content/repositories/releasesurl>
repository>
<snapshotRepository>
<id>nexus-publicid>
<url>http://localhost:8081/nexus/content/repositories/snapshotsurl>
snapshotRepository>
distributionManagement>
project>
注意:如上的 repository的 id 依然是要和settings.xml中配置的server中的id 一致,才能通过私服的认证