maven学习笔记
maven学习笔记
1. 什么是Maven
1.1. Maven的概念
Maven 是自动化构建工具。
Maven 是 Apache 软件基金会组织维护的一款自动化构建工具,专注服务于 Java 平台的项目构建和依赖管理。Maven 这个单词的本意是:专家,内行。
Maven 是目前最流行的自动化构建工具,对于生产环境下多框架、多模块整合开发有重要作用,Maven 是一款在大型项目开发过程中不可或缺的重要工具。
1.2. 为什么要使用Maven
我们知道,项目开发不仅仅是写写代码而已,期间会伴随着各种必不可少的事情要做,例如:
- 我们需要引用各种 jar 包,尤其是比较大的工程,引用的 jar 包往往有几十个乃至上百个,每个都要到不同的官网去下载,而且每次用到的 jar 包,都需要手动引入工程目录,而且经常遇到各种让人抓狂的 jar 包冲突,版本冲突,Maven可以自动下载jar包及依赖包添加到项目中,大大减轻了工作负担。
- 我们开发的 Java 文件,都是需要将它编译成二进制字节码文件。好在这项工作可以由各种集成开发工具帮我们完成,Eclipse、IDEA 等都可以将代码即时编译。但有时候我们需要多个模块同时编译,就必须要借助于Maven工具了。
- 每个项目或模块开发过程中都会有 bug,因此写完了代码,我们还要写一些单元测试,然后一个个的运行来检验代码质量,Maven提供了专门的测试插件来实施测试。
- 再优雅的代码也是要出来卖的。我们后面还需要把代码与各种配置文件、资源整合到一起,定型打包,如果是 web项目,还需要将之发布到服务器进行调用,这些都可以通过Maven轻松搞定。
总之,Maven是项目开发必须不可少的工具。
类似自动化构建工具还有:Gant, Gradle。
1.3. 项目构建过程
构建(build)是面向过程的(从开始到结尾的多个步骤),涉及到多个环节的协同工作
构建过程的几个主要环节
- 清理:删除以前的编译结果,为重新编译做好准备。
- 编译:将Java源程序编译为字节码文件。
- 测试:针对项目中的关键点进行测试,确保项目在迭代开发过程中关键点的正确性。
- 报告:在每一次测试后以标准的格式记录和展示测试结果。
- 打包:将一个包含诸多文件的工程封装为一个压缩文件用于安装或部署。Java 工程对应 jar 包,Web 工程对应war包。
- 安装:在Maven环境下特指将jar包安装到本地仓库中。这样该项目就可以被其他的maven项目通过依赖的方式引入。
- 部署:将jar包部署到私服上。
1.4. Maven的两大核心功能(重点)
1.4.1. 项目构建
对项目进行编译,测试,打包,部署等构建。
1.4.2. 依赖管理
对jar包的统一管理,Maven提供中央仓库,私服,本地仓库解决jar包的依赖和相关依赖的下载。
如下图所示:包括蓝、黄两个部分分别对应着依赖关系和项目构建两大核心功能。
2. Maven的核心概念
2.1. 什么是POM
POM(Project Object Model)项目对象模型,它是Maven的核心组件。它是Maven中的基本工作单元。它是一个xml文件,以pom.xml驻留在项目的根目录中。POM不仅包含有关项目的信息及Maven用于构建项目的各种配置的详细信息,还包含目标和插件。
pom.xml文件示例:
<!--添加父工程的引用-->
<parent>
<groupId>com.bjpowernode.xiaomi</groupId>
<artifactId>xiaomi-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<!--本项目的身份证号gav-->
<groupId>com.bjpowernode</groupId>
<artifactId>springmvc_006_ssm</artifactId>
<version>1.0.0</version>
<!--打包方式-->
<packaging>war</packaging>
<!--集中定义依赖版本号,使用属性-->
<properties>
<!--mysql驱动的依赖-->
<mysql.version>5.1.32</mysql.version>
</properties>
<!--添加依赖-->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
<build>
<!--聚合工程-->
<modules>
<module>xiaomi-manager-pojo</module>
<module>xiaomi-manager-mapper</module>
<module>xiaomi-manager-service</module>
<module>xiaomi-manager-web</module>
</modules>
<!--插件配置-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
<!--指定配置文件识别路径-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
2.2. 什么是约定的目录结构
会有预先约定好的目录结构,必须要遵循的规范,所有的Maven项目都依照这个规范。主要的目的是将项目的源码文件,测试代码,资源文件完全分开,便于项目管理和扩展。
2.3. 什么是坐标GAV
也称为gav定位。使用三个标签来唯一定位jar资源。项目的唯一的名称,创建项目时定义gav名称,引用项目时使用gav名称。相当于项目的身份证号。
坐标就是资源的唯一定位。创建项目时使用坐标拟定一个名称,访问资源时通过坐标找到资源。
表现在仓库,gav是一级一级的目录。
-
groupId: 组织名称,一般是公司域名的倒写 com.bjpowernode
-
artifactId: 项目名称 springmvc_006_ssm
-
version: 版本编号 1.0-SNAPSHOT(开发时的临时版本号) 5.2.5.RELEASE(发布版本)
定义项目
<groupId>com.bjpowernode</groupId> <artifactId>springmvc_006_ssm</artifactId> <version>1.0.0</version>
引用项目
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>springmvc_006_ssm</artifactId>
<version>1.0.0</version>
</dependency>
2.4. 什么是仓库
存放jar包的位置 。Maven中所有的jar包都在仓库中。仓库分为本地仓库和远程仓库。
本机存储jar包的位置称为本地仓库。远程存储jar包称远程仓库。
A.本地仓库:一定要是一个没有中文的路径下。
B.远程仓库:通过局域网或互联网访问的仓库地址。
我们依赖的jar包它从哪儿获取呢?有同学说已经安装了,在它的安装包里啊,大家可以看一下maven下载下来才8M,我们需要的jar包有时候都几百兆甚至几个G,它从哪儿弄去呢? 其实,maven有仓库的概念。在Maven中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。Maven 核心程序仅仅定义了自动化构建项目的生命周期,但具体的构建工作是由特定的构件完成的。而且为了提高构建的效率和构件复用,maven把所有的构件统一存储在某一个位置,这个位置就叫做仓库。
2.4.1. 本地仓库
本地仓库,存在于当前电脑上,默认存放在.m2\repository中,为本机上所有的Maven工程服务。你也可以通过Maven的配置文件Maven_home/conf/settings.xml中修改本地仓库所在的目录。 是用户的主目录,windows系统中是 c:/user/登录系统的用户名
我课程里是存放在本机上的某个磁盘的位置(一定是没有中文的路径). D:\repository
秘密: gav就是仓库中一级一级的目录名称
2.4.2. 远程仓库
远程仓库,分为为全世界范围内的开发人员提供服务的中央仓库、为全世界范围内某些特定的用户提供服务的中央仓库镜像、为本公司提供服务自己架设的私服。
中央仓库是maven默认的远程仓库,其地址是:http://repo.maven.apache.org/maven2/,中央仓库包含了绝大多数流行的开源Java构件,以及源码、作者信息、许可证信息等。一般来说,简单的Java项目依赖的构件都可以在这里下载得到。
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的Maven用户使用。当Maven需要下载构件的时候,它从私服请求,如果私服上不存在该构件,则从外部的远程仓库下载,缓存在私服上之后,再为Maven的下载请求提供服务。我们还可以把一些无法从外部仓库下载到的构件上传到私服上。
程序员常用的一个仓库:
2.5. 什么是依赖
所有的资源是以gav进行定义的,也是通过gav来添加引用.
一个Maven 项目正常运行需要其它项目的支持,Maven 会根据坐标自动到本地仓库中进行查找。对于程序员自己的 Maven 项目需要进行安装,才能保存到仓库中。不用maven 的时候所有的 jar 都不是你的,需要去各个地方下载拷贝,用了 maven 所有的 jar 包都是你的,想用谁,叫谁的名字就行。maven 帮你下载。
除了管理当前要使用的jar包,并且同时管理与其有依赖关系的jar包,自动去下载,并添加到当前的仓库,并给项目添加引用。是通过
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
</dependencies>
2.6. 什么是生命周期
它对应项目构建的过程,底层是Maven命令的调用(插件).
对项目的构建是建立在生命周期模型上的,它明确定义项目生命周期各个阶段,并且对于每一个阶段提供相对应的命令,对开发者而言仅仅需要掌握一小堆的命令就可以完成项目各个阶段的构建工作。
构建项目时按照生命周期顺序构建,每一个阶段都有特定的插件来完成。不论现在要执行生命周期中的哪个阶段,都是从这个生命周期的最初阶段开始的。
对于我们程序员而言,无论我们要进行哪个阶段的构建,直接执行相应的命令即可,无需担心它前边阶段是否构建,Maven 都会自动构建。这也就是 Maven 这种自动化构建工具给我们带来的好处。
使用idea后,生命周期要调用的命令被集成化一些按钮,只需要双击即可调用相应的插件来运行。
生命周期对应的Maven命令(了解):
- mvn clean 清理(会删除原来编译和测试的目录,即 target 目录,但是已经 install 到仓库里的包不会删除)
- mvn compile 编译主程序(会在当前目录下生成一个 target,里边存放编译主程序之后生成的字节码文件)
- mvn test-compile 编译测试程序(会在当前目录下生成一个 target,里边存放编译测试程序之后生成的字节码文件)
- mvn test 测试(会生成一个目录surefire-reports,保存测试结果)
- mvn package 打包主程序(会编译、编译测试、测试、并且按照 pom.xml 配置把主程序打包生成 jar 包或者 war 包)
- mvn install 安装主程序(会把本工程打包,并且按照本工程的坐标保存到本地仓库中)
- mvn deploy 部署主程序(部署到私服仓库中)。
2.7. 什么是插件
Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成,例如编译源代码是由maven- compiler-plugin完成的。进一步说,每个任务对应了一个插件目标(goal),每个插件会有一个或者多个目标,例如maven- compiler-plugin的compile目标用来编译位于src/main/java/目录下的主源码,testCompile目标用来编译位于src/test/java/目录下的测试源码。Maven支持极简化的插件添加.使用
Maven就是一个插件的框架,插件就是集成化的功能.
通过
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
常见的插件:comcat插件,编译插件,MyBatis的逆向工程插件.
3. Maven的应用
3.1. 下载Maven
我们使用的是最新apache-maven-3.9.6的版本,idea使用的是2023.1的版本。
3.2. 配置Maven
要点:一定要将Maven工具解压到一个没有中文的路径下!
3.2.1. 配置环境变量
必须要配置JAVA_HOME和MAVEN_HOME的环境变量。在Maven工具的bin目录下的mvn文件中多次使用这两个环境变量。
配置步骤:
-
我的电脑--->右键属性--->高级系统设置--->环境变量--->系统变量
-
点新建--->
-
创建JAVA_HOME环境变量
变量名: JAVA_HOME 变量值: C:\Program Files\Java\jdk-17
-
创建MAVEN_HOME环境变量
变量名: MAVEN_HOME 变量值: D:\apache-maven-3.9.6 (Maven工具所在的目录,bin的上一级目录)
-
-
点path--->编辑--->新建
- %JAVA_HOME%\bin
- %MAVEN_HOME%\bin
- --->确定--->确定--->确定
-
打开cmd窗口,输入
mvn -v
Apache Maven 3.9.6 (c9616018c7a021c1c39be70fb2843d6f5f9b8a1c)
Maven home: D:\apache-maven-3.9.6
Java version: 17.0.6, vendor: Oracle Corporation, runtime: C:\Program Files\Java\jdk-17
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
有以上输出则表明配置环境变量成功。
3.2.2. 配置Maven工具参数
打开D:\apache-maven-3.9.6\conf\settings.xml文件,进行本地仓库,远程仓库和JDK参数设置。
-
配置本地仓库,将注释中53行的代码提取出注释,放置在第55行,设置本地仓库的地址路径。如果已有本地仓库则直接指定地址,如果没有本地仓库则指定一个目录,在idea配置后会自动生成目录。
-
配置远程仓库
找到结束标签,将以下代码贴在其前面。
<!--配置阿里远程仓库-->
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
远程仓库配置后,经常出现以下bug,连网去点try...就行,如果还是出现try...,就需要到本地仓库中,搜索last*,将出现的所有文件都删除后,再来点try...就行。
-
配置JDK属性
在
标签中进行配置,一定要小心,找到 结束标签,在其前面配置以下代码。因为在
标签中全部是注释,粘到哪里都在注释中,只有找到结束标签前才是注释外的,配置才会生效。 <profile> <id>jdk17</id> <activation> <activeByDefault>true</activeByDefault> <jdk>17</jdk> </activation> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <maven.compiler.compilerVersion>17</maven.compiler.compilerVersion> </properties> </profile>
3.2.3. 为Idea2023当前打开的工作区集成maven
打开idea2023,具体步骤如下:
- File--->settings--->Build,Execution,Depolyment--->Build Tools--->Maven
Maven home path(Maven工具所在的目录,bin的上一级目录): D:\apache-maven-3.9.6
勾选Override选项
User settings file(Maven工具的核心配置文件): D:\apache-maven-3.9.2\conf\settings.xml
Local repository(本地仓库,antlr的上一级目录): D:\repository - Maven--->Importing--->配置JDK的版本
- Runner
VM Options:-DarchetypeCatalog=internal (在新建模块时使用本地模板)
JRE: 17 - Repositories(刷新本地仓库的索引): 选中本地仓库,点Update
3.2.4. 为Idea2023新建项目集成Maven
- File--->New Projects Setup--->Settings for New Project
- 同3.2.2的步骤再来一遍
3.3. 基于Maven开发JavaSE的项目
-
File--->new--->Project--->Empty Project
Location:D:\course\10-Maven\code Name:mavenall --->Create
-
ctrl+alt+shift+s:打开项目结构窗口
Project:设置JDK版本为17
-
Modules:
- ---> New Module
Build system: Maven
-
Advanced Settings
GroupId:com.bjpowernode ArtifactId:maven_001_javase --->Create
-
观察Sources和Dependcies选项的JDK版本,都为17
--->ok
-
补齐目录
在test目录上右键--->new Directory--->resouces
-
修改pom.xml文件
添加单元测试的依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
切记:点pom文件右上角的M标识刷新依赖
-
开发Hello.java类
public class Hello { //加法运算 public int sum(int num1,int num2){ return num1 + num2; } //乘法运算 public int mul(int num1,int num2){ return num1 * num2; } }
-
开发测试类
public class MyTest { /** * 测试功能由测试方法来实现 * 测试方法的规范 * 1)访问权限是public * 2)方法没有返回值void * 3)方法名称自 定义,推荐以test开头 * 4)方法没有参数 * 5)使用@Test注解声明是测试方法 */ @Test public void testSum(){ Hello hello = new Hello(); System.out.println(hello.sum(3,6)); } @Test public void testMul(){ Hello hello = new Hello(); System.out.println(hello.mul(3,6)); } }
4. Maven的依赖管理
在JAVA开发中,项目的依赖管理是一项重要任务。通过合理管理项目的依赖关系,我们可以有效的管理第三方库,模块的引用及版本控制。而Maven作为一个强大的构建工具和依赖管理工具,为我们提供了便捷的方式来管理项目的依赖。
4.1. 什么是依赖范围
Maven的依赖构件包含一个依赖范围的属性。这个属性描述的是三套classpath的控制,即编译、测试、运行。这说白了就是添加的jar包起作用的范围。 maven提供了以下几种依赖范围:compile,test,provided.runtime,system。
分别介绍如下:
- compile
编译依赖范围,如果没有指定,默认使用该依赖范围,对于编译、测试、运行3种classpath都有效。会打进包。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.24</version>
<scope>compile</scope>
</dependency>
</dependencies>
- test
测试依赖范围,使用此依赖范围的maven依赖,只对编译测试、运行测试的classpath有效,在编译主代码、运行项目时无法使用此类依赖。比如junit,它只有在编译测试代码及运行测试的时候才需要。不会打进包。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
- provided
已提供依赖范围。表示项目的运行环境中已经提供了所需要的构件,对于此依赖范围的maven依赖,对于编译源码、编译测试、运行测试中classpath有效,但在运行时无效。比如上面说到的servlet-api,这个在编译和测试的时候需要用到,但是在运行的时候,web容器已经提供了,就不需要maven帮忙引入了。不会打进包。
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
- runtime
运行时依赖范围,使用此依赖范围的maven依赖,对于测试和运行项目的classpath有效,但在编译时无效,比如jdbc驱动实现,项目代码编译的时候只需要提供JDK提供的JDBC接口,运行的时候才需要具体的jdbc驱动实现。
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
<scope>runtime</scope>
</dependency>
</dependencies>
- system
系统依赖范围,该依赖与3中classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显示第指定依赖文件的路径。这种依赖直接依赖于本地路径中的构件,可能每个开发者机器中构件的路径不一致,所以如果使用这种写法,你的机器中可能没有问题,别人的机器中就会有问题,所以建议谨慎使用。
<dependencies>
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>maven_001_javase</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>system</scope>
<systemPath>D:/repository/com/bjpowernode/maven_001_javase/1.0-SNAPSHOT/maven_001_javase-1.0-SNAPSHOT.jar</systemPath>
</dependency>
</dependencies>
总结如下图:
4.2. 什么是依赖传递
依赖具有传递性。 Maven 的依赖传递机制是指:不管 Maven 项目存在多少间接依赖,POM 中都只需要定义其直接依赖,不必定义任何间接依赖,这在一定程序上简化 了POM 的配置。
假项目A依赖项目B,项目B依赖项目C,则A----->直接依赖B,B----->直接依赖C,A----->间接依赖C。
直接依赖和间接依赖是一个相对的概念。直接在项目中配置的依赖称为直接依赖,通过添加依赖关联进来的依赖称为间接依赖。1是项目的直接依赖,2是1的直接依赖,2是项目的间接依赖,以此类推。如图
4.2.1. 依赖范围对依赖传递的影响
B 是 A 的直接依赖,C 是 A 的间接依赖,根据 Maven 的依赖传递机制,间接依赖 C 会以传递性依赖的形式引入到 A 中,但这种引入并不是无条件的,它会受到依赖范围的影响。
图示依赖传递关系:
规范化依赖传递的结果:
交叉部分的单元格的取值为传递性依赖的依赖范围,若交叉单元格取值为 “-”,则表示该传递性依赖不能被传递。
通过上表,可以总结出以下规律:
- 当间接依赖的范围是 compile 时,与直接依赖的范围一致;
- 当间接依赖的范围是 test 或 provided时,传递性依赖不会被传递;
- 当间接依赖的范围是 runtime 时,传递性依赖的范围与直接依赖的范围一致,但 compile 例外,此时传递性依赖的范围为 runtime。
4.3. 依赖冲突
4.3.1. 什么是依赖冲突
在 Maven 项目中,依赖通常被定义在项目的 pom.xml 文件中。当多个依赖项引入了不同版本的相同库时,就会发生依赖冲突。这可能是因为项目的直接依赖和间接依赖导致了同一库的多个版本存在于类路径中。每个显式声明的类包都会依赖于一些其它的隐式类包,这些隐式的类包会被maven间接引入进来,从而造成类包冲突。
4.3.2. 依赖冲突的解决方案
Maven可以通过以下途径解决依赖冲突。
1)版本锁定
2)短路径优先
3)声明优先
4)特殊优先
5)可选依赖
6)排除依赖
4.3.2.1. 版本锁定
在父工程中使用dependencyManagement 进行版本锁定,dependencyManagement可以统一管理整个项目的版本号,确保应用的各个项目的依赖和版本一致。
dependencyManagement只是声明依赖,并不自动实现引入,因此子项目需要显示的声明需要用的依赖,便可以忽略版本号。
如果排斥父工程中定义的版本号,可以显示的进行版本号声明。
- 子工程使用父工程锁定的版本号
- 子工程使用自定义的版本号,只要重新声明即可
- 父工程不使用
标签,则子工程跟父工程完全保持一致。子工程不需要显示依赖任何jar包。
4.3.2.2. 短路径优先
引入路径短者优先,顾名思义,当一个间接依赖存在多条引入路径时,引入路径短的会被解析使用。如图
4.3.2.3. 声明优先
如果存在短路径,则优先选择短路径,如果路径相同的情况下,先声明者优先,POM 文件中依赖声明的顺序决定了间接依赖会不会被解析使用,顺序靠前的优先使用。如图。
注意去掉<scope>标签,否则会因为依赖范围的影响导致效果无法显示。
4.3.2.4. 特殊优先(后来者居上)
同一个pom.xml文件中进行了多次依赖jar包不同版本的配置,后面的覆盖前面的配置。这种情况比较少见。
4.3.2.5. 可选依赖
maven_03项目可选择是否传递间接依赖junit_4.13,主动权在当前项目maven_03中。如果当前项目被依赖到其它项目中,当前项目可以拒绝交出间接依赖项。例如maven_02添加了maven_03的依赖,maven_03可以自主设置其依赖项junit_4.13是否被间接传递。
代码示例:
4.3.2.6. 排除依赖
是当前项目是否主动断开其依赖项目的间接依赖。也就是控制当前项目是否使用其直接依赖传递下来的接间依赖。在maven_02项目中添加maven_03项目的依赖,但不要maven_03项目中的junit_4.13的依赖,可以选择排除依赖。这样可以保证当前项目依赖的纯净性。
排除依赖使用 exclusions 元素排除依赖,说明如下:
-
exclusions 元素下可以包含若干个 exclusion 子元素,用于排除若干个间接依赖,该元素包含两个子元素:groupId 和 artifactId,用来确定需要排除的间接依赖的坐标信息
-
exclusion 元素中只需要设置 groupId 和 artifactId 就可以确定需要排除的依赖,无需指定版本version
如图
代码示例:
4.3.2.7. 可选依赖和排除依赖的区别
排除依赖和可选依赖都能在项目中将间接依赖排除在外,但两者实现机制却完全不一样。
- 可选依赖是自己决定是否向外提供间接依赖(maven_03设置拒绝提供间接依赖junit)
- 排除依赖是主动拒绝添加直接依赖关联的间接依赖(maven_02项目设置排除maven_03的junit依赖)
- 可选依赖的优先级高于排除依赖
- 若对于同一个间接依赖同时使用排除依赖和可选依赖进行设置,那么可选依赖的取值必须为 false,否则排除依赖无法生效。
如果用一个生活中的例子可能更易懂一些。
A组织(maven_02)抓住了B组织(依赖了maven_03),要求供出联系人名单(maven_03中依赖的junit_4.13),最终结果是没有得到联系人名单(不依赖jjunit_4.13)。
可选依赖:是maven_03(B组织人)死活不说联系人的名单junit_4.13(可选,我选择不说)。
排除依赖:是maven_02(A组织)不要(B组织)人提供的联系人名单(排除,我就是不要你的名单, 估计你是乱说的)。
4.4. 刷新依赖的8种方式
在idea中有时候会出现刷新延时的情况,那么需要进行手工刷新依赖。
-
点击M刷新按钮。
-
点Maven窗口的Reload All Maven Projects。
-
Build--->ReBuild Project 重新构建项目的同时刷新所有依赖。
-
点击本项目的pom.xml文件--->右键--->Maven--->Reload Project 刷新本项目的依赖。
-
打开pom.xml文件,全选,拷贝,删除,关闭,打开,粘贴.物理刷新pom.xml文件 。
-
Invalidate Caches--->全选--->Invalidate and Restart 清空idea的缓存并重启idea刷新依赖。
-
打开本地仓库,搜索last,全选删除,点Maven的刷新全部依赖的按钮。
-
在7的步骤后执行File--->settings--->Build,Execution,Deployment--->Build Tools--->Maven--->Repositories--->选中本地仓库--->update--->ok。
4.5. 资源文件的指定
src/main/java 和 src/test/java 这两个目录中的所有.java 文件会分别在 comile 和 test-comiple 阶段被编译,编译结果分别放到了 target/classes 和 targe/test-classes 目录中,但是这两个目录中的其他文件(后缀是.properties或.xml等文件)都会被忽略掉(编译后丢失),如果需要把 src 目录下的除.java之外的文件包放到 target/classes 目录,作为输出的 jar 一部分。需要指定资源文件位置。以下内容放到
代码示例:
添加指定后:
指定代码:
<build>
<resources>
<resource>
<!--指定java目录下的所有路径下的所有文件-->
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<!--指定resources目录下的所有路径下的所有文件-->
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
一般情况下,会两个目录都指定.
5. Maven的继承和聚合
5.1. 什么是继承
Maven 的依赖传递机制可以一定程度上简化 POM 的配置,但这仅限于存在依赖关系的项目或模块中。当一个项目的多个模块都依赖于相同 jar 包的相同版本,且这些模块之间不存在依赖关系,这就导致同一个依赖需要在多个模块中重复声明,这显然是不可取的,大量的前人经验告诉我们,重复往往意味着更多的劳动和更高的潜在风险。
在 Java 面向对象中,我们可以建立一种类的父子结构,然后在父类中声明一些字段和方法供子类继承,这样就可以一定程度上消除重复,做到 “一处声明,多处使用”。在 Maven 的世界中,也有类似的机制,它就是 POM 继承。
Maven 在设计时,借鉴了 Java 面向对象中的继承思想,提出了 POM 继承思想。当一个项目包含多个模块时,可以在该项目中再创建一个父模块,并在其 POM 中声明依赖,其他模块的 POM 可通过继承父模块的 POM 来获得对相关依赖的声明。
对于父模块而言,其目的是为了消除子模块 POM 中的重复配置,其中不包含有任何实际代码,因此父模块 POM 的打包类型(packaging)必须是 pom。
如图所示:
子工程可以继承的父工程的元素(黄色代表常用):
元素 | 描述 |
---|---|
groupId | 项目组 ID,项目坐标的核心元素 |
version | 项目版本,项目坐标的核心元素 |
description | 项目的描述信息 |
organization | 项目的组织信息 |
inceptionYear | 项目的创始年份 |
url | 项目的 URL 地址 |
developers | 项目的开发者信息 |
contributors | 项目的贡献者信息 |
distributionManagement | 项目的部署配置 |
issueManagement | 项目的缺陷跟踪系统信息 |
ciManagement | 项目的持续集成系统信息 |
scm | 项目的版本控制系统信息 |
mailingLists | 项目的邮件列表信息 |
properties | 自定义的 Maven 属性 |
dependencies | 项目的依赖配置 |
dependencyManagement | 项目的依赖管理配置 |
repositories | 项目的仓库配置 |
build | 包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等 |
reporting | 包括项目的报告输出目录配置、报告插件配置等 |
父工程示例:
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>maven_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式必须是pom,声明是父工程-->
<packaging>pom</packaging>
<!--聚合子模块-->
<modules>
<module>maven_son</module>
<module>maven_web</module>
</modules>
<properties>
<!--定义属性,便于版本的管理-->
<spring-version>5.3.24</spring-version>
<servlet-version>4.0.1</servlet-version>
</properties>
<!--只是定义,并没有真正的添加依赖,子工程根据需要有选择的添加依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<!--引用定义好的属性-->
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet-version}</version>
<!--切记切记一定要添加scope的依赖范围 使用部署时的tomcat提供的 -->
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<!--类似dependencyManagement,子工程根据需要有选择的添加插件 -->
<pluginManagement>
<plugins>
<!--只定义Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8089</port>
<url>/</url>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
子工程示例:
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bjpowernode</groupId>
<artifactId>maven_parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<!--可以省略groupId和version,因为与父工程保持一致-->
<artifactId>maven_web</artifactId>
<packaging>war</packaging>
<!--需要什么依赖,添加什么依赖,可以省略版本号,版本由父工程统一管理 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!--配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<!--子工程可以自定义端口号,不自定义则使用父工程的-->
<configuration>
<port>8060</port>
<url>/</url>
</configuration>
</plugin>
</plugins>
</build>
总结:一句话,通过继承可以实现子工程沿用父工程的配置。大大减少重复设置。
5.2. 什么是聚合
使用 Maven 聚合功能对项目进行构建时,需要在该项目中额外创建一个的聚合模块,然后通过这个模块构建整个项目的所有模块。聚合模块仅仅是帮助聚合其他模块的工具,其本身并无任何实质内容,因此聚合模块中只有一个 POM 文件,不包含 src 等目录。
与父模块相似,聚合模块的打包方式(packaging)也是 pom,用户可以在其 POM 中通过 modules 下的 module 子元素来添加需要聚合的模块的目录路径。父模块的 pom.xml 文件的
项目结构:
代码示例:
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>maven_ju</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>../maven_02</module>
<module>../maven_03</module>
<module>../maven_01</module>
<module>../maven_parent</module>
</modules>
代码结构:
运行结果示例:
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] maven_03 [jar]
[INFO] maven_02 [jar]
[INFO] maven_01 [jar]
[INFO] maven_parent [pom]
[INFO] maven_son [jar]
[INFO] maven_web [war]
[INFO] maven_ju [pom]
[INFO]
......
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ maven_03 ---
[INFO] Surefire report directory: E:\BJ2310\10-Maven\04_project\mavenall\maven_03\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running test.MyTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.068 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ maven_03 ---
[INFO] Building jar: E:\BJ2310\10-Maven\04_project\mavenall\maven_03\target\maven_03-1.0-SNAPSHOT.jar
[INFO]
[INFO] ----------------------< com.bjpowernode:maven_02 >----------------------
[INFO] Building maven_02 1.0-SNAPSHOT [2/7]
[INFO] from ..\maven_02\pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven_02 ---
......
Running test.MyTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.055 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ maven_02 ---
[INFO] Building jar: E:\BJ2310\10-Maven\04_project\mavenall\maven_02\target\maven_02-1.0-SNAPSHOT.jar
[INFO]
[INFO] ----------------------< com.bjpowernode:maven_01 >----------------------
[INFO] Building maven_01 1.0-SNAPSHOT [3/7]
[INFO] from ..\maven_01\pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven_01 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
[INFO]
.......
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ maven_01 ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ maven_01 ---
[INFO] Building jar: E:\BJ2310\10-Maven\04_project\mavenall\maven_01\target\maven_01-1.0-SNAPSHOT.jar
[INFO]
[INFO] --------------------< com.bjpowernode:maven_parent >--------------------
[INFO] Building maven_parent 1.0-SNAPSHOT [4/7]
[INFO] from ..\maven_parent\pom.xml
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] ---------------------< com.bjpowernode:maven_son >----------------------
[INFO] Building maven_son 1.0-SNAPSHOT [5/7]
[INFO] from ..\maven_parent\maven_son\pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
......
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ maven_son ---
[INFO] Building jar: E:\BJ2310\10-Maven\04_project\mavenall\maven_parent\maven_son\target\maven_son-1.0-SNAPSHOT.jar
[INFO]
[INFO] ---------------------< com.bjpowernode:maven_web >----------------------
[INFO] Building maven_web 1.0-SNAPSHOT [6/7]
[INFO] from ..\maven_parent\maven_web\pom.xml
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
.......
[INFO] ----------------------< com.bjpowernode:maven_ju >----------------------
[INFO] Building maven_ju 1.0-SNAPSHOT [7/7]
[INFO] from pom.xml
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for maven_ju 1.0-SNAPSHOT:
[INFO]
[INFO] maven_03 ........................................... SUCCESS [ 2.506 s]
[INFO] maven_02 ........................................... SUCCESS [ 0.609 s]
[INFO] maven_01 ........................................... SUCCESS [ 0.083 s]
[INFO] maven_parent ....................................... SUCCESS [ 0.000 s]
[INFO] maven_son .......................................... SUCCESS [ 0.127 s]
[INFO] maven_web .......................................... SUCCESS [ 1.043 s]
[INFO] maven_ju ........................................... SUCCESS [ 0.002 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.543 s
[INFO] Finished at: 2024-03-16T00:07:25+08:00
[INFO] ------------------------------------------------------------------------
总结:一句话,聚合就是集中构建项目的。
6. Maven的私服
6.1. 什么是私服
Maven 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的远程仓库(中央仓库、其他远程公共仓库)。一些无法从外部仓库下载到的构件,也能从本地上传到私服供其他人使用。
Maven 私服其实并不是 Maven 的核心概念,它仅仅是一种衍生出来的特殊的仓库,但这并不代表它不重要,相反由于私服具有降低中央仓库负荷、节省外网带宽、以及提高项目稳定性等优点,使得私服在实际开发过程中得到了相当普遍地使用。建立了 Maven 私服后,当局域网内的用户需要某个构件时,会先请求本地仓库,若本地仓库不存在所需构件,则请求 Maven 私服,将所需构件下载到本地仓库,若私服中不存在所需构件,再去请求外部的远程仓库,将所需构件下载并缓存到 Maven 私服,若外部远程仓库不存在所需构件,则 Maven 直接报错。
6.2. Maven仓库管理器Nexus
6.2.1. 什么是Nexus
Nexus 是 Sonatype 公司发布的一款仓库(Repository)管理软件,常用来搭建 Maven 私服,所以也有人将 Nexus 称为“Maven仓库管理器”。 Sonatype Nexus 是当前最流行,使用最广泛的 Maven 仓库管理器。Nexus 分为开源版和专业版,其中开源版足以满足大部分 Maven 用户的需求。
6.2.2. Nexus仓库的类型
Nexus默认内置了很多仓库,这些仓库可以划分为3种类型,每种类型的仓库用于存放特定的jar包,具体说明如下:
- hosted:宿主仓库,部署自己的jar包到这个类型的仓库中,包括Releases和Snapshots两部分,Releases为公司内部发行版本仓库,Snapshots为公司内部测试版本仓库。
- proxy: 代理仓库,用于代理远程的公共仓库,如Maven中央仓库,用户连接私服,私服自动去中央仓库下载Jar包或者插件。
- group:仓库组,用来合并多个hosted/proxy仓库,通常用于配置自己的Maven连接仓库组。当我们下载jar包时,从仓库组中的每个仓库找一遍,找到后下载。
6.2.3. 如何搭建私服
- 下载Nexus
官网地址:https://help.sonatype.com/repomanager3/download
- 安装Nexus
下载后解压到一个没有中文的路径下:
进入到D:\nexus-3.66.0-02\bin目录下:
双击nexus.exe没有反应,需要以管理员身份打开cmd窗口,输入命令: nexus /run 回车,需要等待一段时间,直到出现Started Sonatype Nexus OSS 3.66.0-02说明安装成功。
- 访问Nexus
端口号可以改变,在D:\nexus-3.66.0-02\etc目录下的nexus-default.properties文件中进行修改。
注意:如果浏览器访问一直转圈打不开页面,则在cmd窗口ctrl+c唤醒一下就可以打开了。
6.3. Nexus私服的应用
6.3.1. 登录
密码所在的文件:
点Browse观察
6.3.2. 设置仓库
-
创建仓库
-
将创建好的仓库添加到public组中。
3.修改中央仓库的地址为阿里云仓库的地址
http://maven.aliyun.com/nexus/content/groups/public/
6.3.3. 使用Nexus下载jar包
创建一个新的本地仓库,用来接收Nexus下载下来的jar包。
因为要使用Maven工具访问私服,所以settings.xml文件要修改。
-
本地仓库地址修改为新地址
<localRepository>E:\repository_nexus</localRepository>
-
设置
标签为Nexus的maven-public库地址(仓库组) <mirror> <id>dljd-nexus</id> <mirrorOf>central</mirrorOf> <name>mynexus</name> <url>http://localhost:8081/repository/maven-public/</url> </mirror>
其中
-
设置每次访问Nexus的用户名和密码,修改settings.xml文件中的
标签 <server> <id>dljd-nexus</id> <username>admin</username> <password>admin</password> </server>
- 打开Idea2023,确定是新的本地仓库地址
选择一个项目maven_01执行clean操作。
观察本地仓库:
观察私服Nexus上的maven-public组
6.3.4. 使用Idea部署jar包到Nexus私服
私服Nexus是部署在局域网的,是全公司共享的仓库地址,每个团队都可以将已完成的功能或测试版本发布到私服供别人来使用。
-
打开要部署的项目的pom.xml文件,设置上传路径
<distributionManagement> <repository> <id>dljd-nexus</id> <url>http://localhost:8081/repository/dljd-release/</url> </repository> <snapshotRepository> <id>dljd-nexus</id> <url>http://localhost:8081/repository/dljd-snapshot/</url> </snapshotRepository> </distributionManagement>
其中
- 运行deploy部署命令
- 观察私服对应仓库变化
release项目部署
snapshot项目部署
总结:整个Maven工具的应用就讲完了。其中一二三章是基础应用,四五六章是高级应用,工作时遇到的问题四五六章有解决方案,到时候再看也来得及。