一、Maven简介
(一)为什么使用 Maven
由于 Java 的生态非常丰富,无论你想实现什么功能,都能找到对应的工具类,这些工具类都是以 jar 包的形式出现的,例如 Spring,SpringMVC、MyBatis、数据库驱动,等等,都是以 jar 包的形式出现的,jar 包之间会有关联,在使用一个依赖之前,还需要确定这个依赖所依赖的其他依赖,所以,当项目比较大的时候,依赖管理会变得非常麻烦臃肿,这是 Maven 解决的第一个问题。
Maven 还可以处理多模块项目。简单的项目,单模块分包处理即可,如果项目比较复杂,要做成多模块项目,例如一个电商项目有订单模块、会员模块、商品模块、支付模块...,一般来说,多模块项目,每一个模块无法独立运行,要多个模块合在一起,项目才可以运行,这个时候,借助 Maven 工具,可以实现项目的一键打包。
(二)Maven是什么
https://maven.apache.org/
Maven 是一个项目管理和构建自动化工具,旨在简化 Java 项目的构建过程。它可以帮助开发人员管理项目的依赖关系、构建项目、发布项目等。Maven 使用基于项目对象模型(Project Object Model,POM)的概念来描述项目,通过一个 XML 文件(通常命名为 `pom.xml`)来配置项目的构建和管理。
三、Maven常用参数和命令
Maven的配置如果全局配置与局部配置冲突的时候,默认局部的优先级高于全局的优先级。
(一)setting.xml参数
<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
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers/>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles/>
</settings>
-
servers配置:鉴权
<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
https://maven.apache.org/xsd/settings-1.0.0.xsd">
...
<!--配置服务端的一些设置。一些设置如安全证书不应该和pom.xml一起分发。这种类型的信息应该存在于构建服务器上的settings.xml文件中。 -->
<servers>
<!--服务器元素包含配置服务器时需要的信息 -->
<server>
<!--这是server的id(注意不是用户登陆的id),该id与distributionManagement中repository元素的id相匹配。 -->
<id>server001</id>
<!--鉴权用户名。鉴权用户名和鉴权密码表示服务器认证所需要的登录名和密码。 -->
<username>my_login</username>
<!--鉴权密码 。鉴权用户名和鉴权密码表示服务器认证所需要的登录名和密码。密码加密功能已被添加到2.1.0 +。详情请访问密码加密页面 -->
<password>my_password</password>
<!--鉴权时使用的私钥位置。和前两个元素类似,私钥位置和私钥密码指定了一个私钥的路径(默认是${user.home}/.ssh/id_dsa)以及如果需要的话,一个密语。将来passphrase和password元素可能会被提取到外部,但目前它们必须在settings.xml文件以纯文本的形式声明。 -->
<privateKey>${usr.home}/.ssh/id_dsa</privateKey>
<!--鉴权时使用的私钥密码。 -->
<passphrase>some_passphrase</passphrase>
<!--文件被创建时的权限。如果在部署的时候会创建一个仓库文件或者目录,这时候就可以使用权限(permission)。这两个元素合法的值是一个三位数字,其对应了unix文件系统的权限,如664,或者775。 -->
<filePermissions>664</filePermissions>
<!--目录被创建时的权限。 -->
<directoryPermissions>775</directoryPermissions>
</server>
</servers>
...
</settings>
2.mirrors:公司内部仓库地址配置
<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
https://maven.apache.org/xsd/settings-1.0.0.xsd">
...
<mirrors>
<!-- 给定仓库的下载镜像。 -->
<mirror>
<!-- 该镜像的唯一标识符。id用来区分不同的mirror元素。 -->
<id>others</id>
<!-- 镜像名称 -->
<name>PlanetMirror Australia</name>
<!-- 该镜像的URL。构建系统会优先考虑使用该URL,而非使用默认的服务器URL。 -->
<url>公司仓库地址</url>
<!-- 被镜像的服务器的id。例如,如果我们要设置了一个Maven中央仓库(http://repo.maven.apache.org/maven2/)的镜像,就需要将该元素设置成central。这必须和中央仓库的id central完全一致。 -->
<mirrorOf>*,!central,!miremote,!snapshots</mirrorOf>
</mirror>
</mirrors>
...
</settings>
3. profiles:根据环境参数来调整构建配置的列表
Profiles(配置文件)是 Maven 中用于在不同环境下自定义构建和运行行为的机制。它允许用户根据特定的条件来定义一组配置,这些条件可以是环境、操作系统、用户、属性等。Profiles 允许用户根据需要激活或取消激活不同的配置,从而实现在不同环境下定制化项目的构建和行为。
主要用途包括:
环境配置: 您可以根据开发、测试和生产等不同的环境,定义不同的构建配置。例如,在开发环境中可能需要启用一些调试或性能监视工具,而在生产环境中则需要优化和简化构建过程。
构建参数: 您可以根据传递给 Maven 命令的参数来激活或取消激活 profiles。这样,您可以根据需要为不同的构建情况提供不同的配置。
依赖项管理: 您可以在不同的 profiles 中定义不同的依赖项,以便根据不同的构建条件使用不同的依赖项版本或替代依赖项。
插件配置: 您可以在 profiles 中配置不同的插件,以便根据构建条件应用不同的插件配置。
其他设置: 您可以根据需要定义其他配置,如资源过滤、源代码的过滤、部署路径等。
<profiles>
<profile>
<!-- profile的唯一标识 -->
<id>test</id>
<!-- 自动触发profile的条件逻辑 -->
<activation />
<!-- 扩展属性列表 -->
<properties />
<!-- 远程仓库列表 -->
<repositories />
<!-- 插件仓库列表 -->
<pluginRepositories />
</profile>
</profiles>
公司外的仓库地址
有时公司仓库里没有某个工具,需要从外部更大的仓库查找。
(一般在国内的话选阿里云的中央仓库,在新加坡的话,我发现maven中央仓库下载更快一些)
在 Maven 的 settings.xml
文件中,您可以通过 <mirrors>
元素配置镜像仓库,将中央仓库指向公司内部的镜像或其他可用的镜像。另外,您还可以通过 <profiles>
元素配置仓库地址,以确保在需要时可以访问中央仓库。
<profiles>
<profile>
<id>company-profile</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
</profile>
</profiles>
(二)项目pom.xml配置
-
项目本身的groupID等信息
pom.xml文件最开始一般会有自己该项目的信息,以下这三个字段。为的是自己的代码install后,贡献到公司的仓库里的时候,别人可以用这几个参数找到自己写的这个项目。
<?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>
...
<groupId>com.xxx</groupId>
<artifactId>groject_name</artifactId>
<version>1.0-SNAPSHOT</version>
2. properties
定义pom上下文常量,在pom中的其它地方可直接引用,方便统一修改版本号。引用方式:${参数名}
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<flink.version>1.12.2-mdh1.12.2.0-SNAPSHOT</flink.version>
<java.version>1.8</java.version>
<scala.binary.version>2.11</scala.binary.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
3. 项目的dependecies(最重要)
引入三方类库
<!-- 该元素描述了项目相关的所有依赖。 这些依赖组成了项目构建过程中的一个个环节。它们自动从项目定义的仓库中下载。
要获取更多信息,请看项目依赖机制。 -->
<dependencies>
<dependency>
<!-- 依赖的group ID -->
<groupId> org.apache.maven </groupId>
<!-- 依赖的artifact ID -->
<artifactId> maven-artifact </artifactId>
<!-- 依赖的版本号。 在Maven 2里, 也可以配置成版本号的范围。 -->
<version> 3.8.1 </version>
<!-- 依赖类型,默认类型是jar。它通常表示依赖的文件的扩展名,但也有例外。一个类型可以被映射成另外一个扩展
名或分类器。类型经常和使用的打包方式对应,尽管这也有例外。一些类型的例子:jar,war,ejb-client和test-jar。
如果设置extensions为 true,就可以在plugin里定义新的类型。所以前面的类型的例子不完整。 -->
<type> jar </type>
<!-- 依赖的分类器。分类器可以区分属于同一个POM,但不同构建方式的构件。分类器名被附加到文件名的版本号后面。例如,
如果你想要构建两个单独的构件成JAR,一个使用Java 1.4编译器,另一个使用Java 6编译器,你就可以使用分类器来生
成两个单独的JAR构件。 -->
<classifier></classifier>
<!-- 依赖范围。在项目发布过程中,帮助决定哪些构件被包括进来。欲知详情请参考依赖机制。
- compile :默认范围,用于编译
- provided:类似于编译,但支持你期待jdk或者容器提供,类似于classpath
- runtime: 在执行时需要使用
- test: 用于test任务时使用
- system: 需要外在提供相应的元素。通过systemPath来取得
- systemPath: 仅用于范围为system。提供相应的路径
- optional: 当项目自身被依赖时,标注依赖是否传递。用于连续依赖时使用 -->
<scope> test </scope>
<!-- 仅供system范围使用。注意,不鼓励使用这个元素,并且在新的版本中该元素可能被覆盖掉。该元素为依赖规定了文件
系统上的路径。需要绝对路径而不是相对路径。推荐使用属性匹配绝对路径,例如${java.home}。 -->
<systemPath></systemPath>
<!-- 当计算传递依赖时, 从依赖构件列表里,列出被排除的依赖构件集。即告诉maven你只依赖指定的项目,不依赖项目的
依赖。此元素主要用于解决版本冲突问题 -->
<exclusions>
<exclusion>
<artifactId> spring-core </artifactId>
<groupId> org.springframework </groupId>
</exclusion>
</exclusions>
<!-- 可选依赖,如果你在项目B中把C依赖声明为可选,你就需要在依赖于B的项目(例如项目A)中显式的引用对C的依赖。
可选依赖阻断依赖的传递性。 -->
<optional> true </optional>
</dependency>
</dependencies>
4. build
<build>
元素是 Maven POM 文件中的一个重要部分,用于配置项目的构建过程。<build>
元素定义了 Maven 如何构建项目,包括编译代码、运行测试、打包应用程序等操作。
Dependency和Plugin的区别
依赖项(Dependencies):
作用: 依赖项是指项目所依赖的外部库、组件或模块,它们是项目构建和运行的基础。例如,如果您的项目使用了 Spring 框架,则需要将 Spring 相关的库作为项目的依赖项。
配置方式: 依赖项通过
<dependencies>
元素在pom.xml
文件中进行配置。每个依赖项由一个<dependency>
元素表示,其中包含了依赖项的坐标(groupId、artifactId、version)以及其他相关信息。作用范围(Scope): 依赖项可以有不同的作用范围,例如
compile
、provided
、runtime
、test
等。作用范围指定了依赖项在项目构建和运行过程中的可见性和生命周期。插件(Plugins):
作用: 插件是用于执行构建过程中各种任务的工具,如编译、测试、打包、部署等。插件提供了 Maven 构建过程中所需的各种功能。
配置方式: 插件通过
<build><plugins>
元素在pom.xml
文件中进行配置。每个插件由一个<plugin>
元素表示,其中包含了插件的坐标(groupId、artifactId、version)以及其他相关信息。执行目标(Goals): 插件定义了一系列可执行的目标(Goals),用于执行特定的任务。例如,
maven-compiler-plugin
插件定义了compile
目标用于编译源代码,maven-surefire-plugin
插件定义了test
目标用于运行测试等。
<!-- 构建项目需要的信息 -->
<build>
<!-- 该元素设置了项目源码目录,当构建项目的时候,构建系统会编译目录里的源码。该路径是相对
于pom.xml的相对路径。默认情况下,Maven 将源代码放置在 src/main/java 目录下 -->
<sourceDirectory></sourceDirectory>
<!-- 该元素设置了项目脚本源码目录,该目录和源码目录不同:绝大多数情况下,该目录下的内容会
被拷贝到输出目录(因为脚本是被解释的,而不是被编译的)。默认情况下,Maven 将脚本源代码放置在 src/main/scripts 目录下 -->
<scriptSourceDirectory></scriptSourceDirectory>
<!-- 该元素设置了项目单元测试使用的源码目录,当测试项目的时候,构建系统会编译目录里的源码。
该路径是相对于pom.xml的相对路径。 -->
<testSourceDirectory></testSourceDirectory>
<!-- 被编译过的应用程序class文件存放的目录。默认情况下,Maven 将编译生成的类文件输出到 target/classes 目录下 -->
<outputDirectory></outputDirectory>
<!-- 被编译过的测试class文件存放的目录。 -->
<testOutputDirectory></testOutputDirectory>
<!-- 使用来自该项目的一系列构建扩展 -->
<extensions>
<!-- 描述使用到的构建扩展。 -->
<extension>
<!-- 构建扩展的groupId -->
<groupId></groupId>
<!-- 构建扩展的artifactId -->
<artifactId></artifactId>
<!-- 构建扩展的版本 -->
<version></version>
</extension>
</extensions>
<!-- 当项目没有规定目标(Maven2 叫做阶段)时的默认值 -->
<defaultGoal></defaultGoal>
<!-- 这个元素描述了项目相关的所有资源路径列表,例如和项目相关的属性文件,这些资源被包含在
最终的打包文件里。 -->
<resources>
<!-- 这个元素描述了项目相关或测试相关的所有资源路径 -->
<resource>
<!-- 描述了资源的目标路径。该路径相对target/classes目录(例如${project.build.outputDirectory})。
举个例子,如果你想资源在特定的包里(org.apache.maven.messages),你就必须该元素设置为
org/apache/maven/messages。然而,如果你只是想把资源放到源码目录结构里,就不需要该配置。 -->
<targetPath></targetPath>
<!-- 是否使用参数值代替参数名。参数值取自properties元素或者文件里配置的属性,文件在filters元素
里列出。 -->
<filtering></filtering><!-- 描述存放资源的目录,该路径相对POM路径 -->
<directory></directory><!-- 包含的模式列表,例如**/*.xml. -->
<includes><include></include></includes><!-- 排除的模式列表,例如**/*.xml --><excludes><exclude></exclude></excludes></resource>
</resources>
<!-- 这个元素描述了单元测试相关的所有资源路径,例如和单元测试相关的属性文件。 -->
<testResources>
<!-- 这个元素描述了测试相关的所有资源路径,参见build/resources/resource元素的说明 -->
<testResource>
<!-- 描述了测试相关的资源的目标路径。该路径相对target/classes目录(例如${project.build.outputDirectory})。
举个例子,如果你想资源在特定的包里(org.apache.maven.messages),你就必须该元素设置为
org/apache/maven/messages。然而,如果你只是想把资源放到源码目录结构里,就不需要该配置。 -->
<targetPath></targetPath>
<!-- 是否使用参数值代替参数名。参数值取自properties元素或者文件里配置的属性,文件在filters元素
里列出。 -->
<filtering></filtering><!-- 描述存放测试相关的资源的目录,该路径相对POM路径 -->
<directory></directory><!-- 包含的模式列表,例如**/*.xml. -->
<includes><include></include></includes><!-- 排除的模式列表,例如**/*.xml --><excludes><exclude></exclude></excludes>
</testResource>
</testResources>
<!-- 构建产生的所有文件存放的目录 默认的 <directory> 是 ${basedir}/target-->
<directory></directory>
<!-- 产生的构件的文件名,默认值是${artifactId}-${version}。 -->
<finalName></finalName>
<!-- 当filtering开关打开时,使用到的过滤器属性文件列表 -->
<filters></filters>
<!-- 子项目可以引用的默认插件信息。该插件配置项直到被引用时才会被解析或绑定到生命周期。给定插件的任何本
地配置都会覆盖这里的配置 -->
<pluginManagement>
<!-- 使用的插件列表 。 -->
<plugins>
<!-- plugin元素包含描述插件所需要的信息。 -->
<plugin>
<!-- 插件在仓库里的group ID -->
<groupId></groupId>
<!-- 插件在仓库里的artifact ID -->
<artifactId></artifactId>
<!-- 被使用的插件的版本(或版本范围) -->
<version></version>
<!-- 是否从该插件下载Maven扩展(例如打包和类型处理器),由于性能原因,只有在真需要下载时,该
元素才被设置成enabled。 -->
<extensions>true/false</extensions>
<!-- 在构建生命周期中执行一组目标的配置。每个目标可能有不同的配置。 -->
<executions>
<!-- execution元素包含了插件执行需要的信息 -->
<execution>
<!-- 执行目标的标识符,用于标识构建过程中的目标,或者匹配继承过程中需要合并的执行目标 -->
<id></id><!-- 绑定了目标的构建生命周期阶段,如果省略,目标会被绑定到源数据里配置的默认阶段 -->
<phase></phase><!-- 配置的执行目标 -->
<goals></goals>
<!-- 配置是否被传播到子POM -->
<inherited>true/false</inherited>
<!-- 作为DOM对象的配置 -->
<configuration></configuration></execution>
</executions>
<!-- 项目引入插件所需要的额外依赖 -->
<dependencies><!-- 参见dependencies/dependency元素 -->
<dependency>
</dependency>
</dependencies>
<!-- 任何配置是否被传播到子项目 -->
<inherited>true/false</inherited><!-- 作为DOM对象的配置 -->
<configuration></configuration></plugin>
</plugins>
</pluginManagement>
<!-- 该项目使用的插件列表 。 -->
<plugins>
<!-- plugin元素包含描述插件所需要的信息。 -->
<plugin>
<!-- 插件在仓库里的group ID -->
<groupId></groupId>
<!-- 插件在仓库里的artifact ID -->
<artifactId></artifactId>
<!-- 被使用的插件的版本(或版本范围) -->
<version></version>
<!-- 是否从该插件下载Maven扩展(例如打包和类型处理器),由于性能原因,只有在真需要下载时,该
元素才被设置成enabled。 -->
<extensions>true/false</extensions>
<!-- 在构建生命周期中执行一组目标的配置。每个目标可能有不同的配置。 -->
<executions>
<!-- execution元素包含了插件执行需要的信息 -->
<execution>
<!-- 执行目标的标识符,用于标识构建过程中的目标,或者匹配继承过程中需要合并的执行目标 -->
<id></id><!-- 绑定了目标的构建生命周期阶段,如果省略,目标会被绑定到源数据里配置的默认阶段 -->
<phase></phase><!-- 配置的执行目标 -->
<goals></goals>
<!-- 配置是否被传播到子POM -->
<inherited>true/false</inherited>
<!-- 作为DOM对象的配置 -->
<configuration></configuration></execution>
</executions>
<!-- 项目引入插件所需要的额外依赖 -->
<dependencies><!-- 参见dependencies/dependency元素 -->
<dependency>
</dependency>
</dependencies>
<!-- 任何配置是否被传播到子项目 -->
<inherited>true/false</inherited>
<!-- 作为DOM对象的配置 -->
<configuration></configuration>
</plugin>
</plugins>
</build>
(三)打包命令
以下是命令行打包命令。也可用IDE自带的快捷方式操作
mvn clean: 清理项目构建过程中生成的临时文件和目录,包括 target 目录下的所有内容。
mvn compile: 编译项目的源代码,将源代码编译成目标代码(如 Java 字节码)。
mvn test: 运行项目的单元测试,执行项目中的测试用例。
mvn package: 打包项目,将编译后的代码、资源文件等打包成一个可执行的 JAR、WAR 或其他类型的文件。
mvn install: 安装项目,将打包好的项目安装到本地 Maven 仓库,以便其他项目可以引用。
mvn deploy: 部署项目,将打包好的项目发布到远程 Maven 仓库,以便其他开发人员或团队可以访问和使用。
四、如何在artifacts上查找自己需要的依赖
至于group ID 和aritifact id 怎么知道,可以谷歌,或者问GPT。查找到后,在公司仓库里找一个相对新的稳定版本。
五、依赖冲突解决
jar包冲突是一个包引入了多次导致的编译或运行时候选出错误版本的问题。
是开发中最常见也是比较头疼的问题。
一般出现的现象是找不到某个类或者方法(而不是找到多个),因为编译时选择了错误的版本,该版本中没有该方法。
解决的方法是
-
将冲突的版本排除掉
-
单独将该子级的工具包放到上层引用 ,短链路优先,会覆盖掉深链路低版本
-
所有的包都用最新版,彻底解决冲突问题。
我之前写过一篇定位jar包冲突的文章,讲了常用的定位方法。可做参考。
java.lang.NoClassDefFoundError: scala/math/PartialOrdering$class --jar包冲突如何识别和解决_jar包java.lang.noclassdeffounderror、-CSDN博客