史上最全Maven教程,没有之一(逃)
lipiwang 2025-05-22 11:40 3 浏览 0 评论
如果你是有一定的开发经验,我相信你一定被项目 lib 下的 JAR 包折磨过,如果碰上兼容问题,更是逐个下载不同版本 JAR 包进行替换排查,相信是每个程序员都不想再经历一边的噩梦。
Maven 的出现则大大降低开发人员的准备工作,让开发人员更专心与业务,下面即介绍 Maven 基本使用。
Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。
一、基础配置
1. 仓库配置
在 Maven 中引入了仓库的概念,开发人员将所编写的 JAR 按照相应格式推送到仓库中,当其他开发者需要引用这个 jar 包时在工程中引用相应依赖,则会先从中央仓库进行下载到本地仓库,此时项目将读取本地仓库的内容。
对于部分组织或机构通常会在此基础上额外搭建私人仓库,在引用依赖时会先从私人仓库进行读取,如果未找到再从中央仓库下载至私人仓库,最后再下载到本地仓库。
通过这种方式开发者则无需再手动管理繁杂的项目 JAR 包,从而实现更高的效率。
2. 基本信息
一个最基本的 Maven 项目通常应包含如下内容,当我们引用一个模块时,也是通过 groupId、 artifactId、 version 三项内容进行确定。
标 签 | 作 用 |
groupId | 通常为组织或公司域名反写。 |
artifactId | 项目的名称。 |
version | 项目的版本信息。 |
name | 项目的简称。 |
description | 项目的简要描述。 |
下面是一个基本定义示例:
xml复制代码<?xml version="1.0" encoding="UTF-8"?>
<project ...>
<!-- 固定 4.0.0, 指定了当前 POM 模型的版本 -->
<modelVersion>4.0.0</modelVersion>
<groupId>xyz.ibudai</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>maven demo</version>
<description>This is maven demo.</description>
</project>
二、依赖管理
1. 依赖引入
通过 dependencies 标签我们即可导入所需要的工程依赖。
xml复制代码<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
<scope>runtime</scope>
</dependency>
</dependencies>
其中 scope 的可选值如下:
作用域 | 效果 |
compile | 编译时需要用到该 JAR 包(默认) |
runtime | 编译时不需要,但运行时需要用到。 |
provided | 编译时需要用到,但运行时由 JDK 或某个服务器提供。 |
test | 编译Test时需要用到该 JAR 包。 |
2. 间接依赖
当项目需要引用到其它依赖时,只需指定所依赖的工程的基本信息即可,剩下的一切都交给 Maven 处理。即便是所要依赖的工程依赖了其它工程,我们也只需引入项目所直接的依赖的工程。
如下图示例中 Dependency-A 引用了 Dependency-B ,而 Dependency-B 又依赖于 Dependency-C ,在传统项目中若在 Dependency-A 中引用 Dependency-B 则需要同时手动添加 Dependency-B 与 Dependency-C 所对应的 JAR 包,但在 Maven 中我们只需要引入 Dependency-B 即可, Mavne 会自动将子模块所依赖的包导入。
- 依赖顺序
- 在 maven 工程中遵循先定义先导入的原则,即当存在多个相同间接依赖,优先导入其父依赖定义在前的简洁依赖。
- 举个例子,如工程中引入 Dependency-A 与 Dependency-B 两个依赖,二者又分别引用了不同版本的 Dependency-C ,但对于 Maven 而言最终编译时同一个依赖即便是不同的版本也只会选择一份。
- 其计算规则如下:若 Dependency-A 定义在 Dependency-B 之前则最终将导入 Dependency-A 中的 C-1.0 版本。而在右侧图例中虽然 Dependency-A 引入优先级高于 Dependency-B ,但是 C-2.0 的间接依赖层级高于 C-1.0,因此将导入 C-2.0 版本。
3. 依赖排除
在引用多个模块时可能会发生版本兼容冲突问题,通过 excludes 标签即可实现依赖排除。
如下我们在工程中引入了 demo-a 依赖,但其又引用 dependency-b 依赖,如想要在当前工程中移除 dependency-b 依赖,此时即可通过 excludes 标签将 dependency-b 排除依赖。
xml复制代码<dependencies>
<dependency>
<groupId>xyz.ibudai</groupId>
<artifactId>demo-a</artifactId>
<version>1.0.0</version>
<excludes>
<exclude>
<groupId>xyz.ibudai</groupId>
<artifactId>dependency-b</artifactId>
<version>1.0.0</version>
</exclude>
</excludes>
</dependency>
</dependencies>
除了手动通过 excludes 标签排除依赖,被引模块也可以在导入依赖时通过 optional 标签禁用依赖传递。
上述示例中若在 demo-a 工程中引入 dependency-b 依赖时添加 optional 标签,那么其它工程在引入 demo-a 依赖时将不会将 dependency-b 作为间接依赖导入。
xml复制代码<dependencies>
<dependency>
<groupId>xyz.ibudai</groupId>
<artifactId>demo-b</artifactId>
<version>1.0.0</version>
<optional>true</optional>
</dependency>
</dependencies>
4. 变量配置
当项目中引入了大量依赖,为了方便管理通常将引入依赖的版本通过变量进行统一配置,从而实现更直观的依赖管理。
通过 properties 标签即可自定义变量配置,然后使用 ${} 引用变量。
xml复制代码<properties>
<mysql.version>8.0.30</mysql.version>
<junit.version>4.13.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- 使用 "${}" 引用上述自定义变量 -->
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
三、模块配置
1. 模块管理
当我们项目包含多个子项目时,通过 modules 标签即可实现模块管理。
xml复制代码<!-- maven-demo pom.xml -->
<modules>
<module>module-1</module>
<module>module-2</module>
</modules>
如下在 maven-demo 中又包含了 module-1 和 module-2 两个工程。
2. 模块继承
通过 parent 即可标记当前模块的父模块,且子模块将会继承父模块中的所有依赖配置。子模块若没有指定的 groupId 和 version 默认继承父模块中的配置。
其中 relativePath 用于指定父模块的 POM 文件目录,省略时默认值为 ../pom.xml 即当前目录的上一级中,若仍未找到则会在本地仓库中寻找。
xml复制代码<!-- module-1 pom.xml -->
<parent>
<groupId>xyz.ibudai</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>module-1</artifactId>
四、统一管理
1. 依赖管理
当一共项目包含多个模块,且多个模块引用了相同依赖时显然重复引用是不太合适的,而通过 dependencyManagement 即可很好的解决依赖共用的问题。
将项目依赖统一定义在父模块的 dependencyManagement 标签中,子模块只需继承父模块并在 dependencies 引入所需的依赖,便可自动读取父模块 dependencyManagement 所指定的版本。
dependencyManagement 既不会在当前模块引入依赖,也不会给其子模块引入依赖,但其可以被继承的,只有在子模块下同样声明了该依赖,才会引入到模块中,子模块中只需在依赖中引入 groupId 与 artifactId 即可, 也可以指定版本则会进行覆盖。
2. 模块示例
接下来以下图中的模块层级关系进行举例:
- maven-demo
- 在 maven-demo 的 dependencyManagement 定义 mysql 和 junit 两个依赖。
- xml复制代码
- <dependencyManagement> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> </dependencies> </dependencyManagement>
- module-1
- 在 module-1 中继承 maven-demo 工程,引入 mysql,无需指定版本,将会自动读取父模块中 dependencyManagement 中所指定的版本。当然你也可以选择指定版本,则将会进行覆盖,但并不建议这么操作,将提高项目维护难度。
- module-1 的 pom 文件内容如下:
- xml复制代码
- <parent> <groupId>xyz.ibudai</groupId> <artifactId>maven-demo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
- module-2
- 在 module-2 配置同 module-1,通过 dependencyManagement 我们即实现了项目依赖版本的统一管理。
- xml复制代码
- <parent> <groupId>xyz.ibudai</groupId> <artifactId>maven-demo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies>
3. 依赖导入
上面介绍了如何通过 dependencyManagement 实现全局的依赖版本管理,但如果工程中的两个子模块都需要配置相同的 dependencyManagement 配置时,当然你可以选择通过继承父模块来实现,也可以用笨办法直接复制粘贴一份。
在上述的 maven-demo 创建同级模块 maven-demo1 ,如果要实现 maven-demo 中配置的 dependencyManagement 则在其 dependencyManagement 配置中导入 maven-demo 并将 scope 设置为 import,并将 type 设置为 pom。
通过导入即可实现更轻量化的模块信息继承,具体配置内容如下:
xml复制代码<dependencyManagement>
<dependencies>
<dependency>
<groupId>xyz.ibudai</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<!-- 导入目标模块的 dependencyManagement -->
<!-- 依赖范围为 import -->
<scope>import</scope>
<!-- 类型一般为 pom -->
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
五、插件管理
经过前面的介绍相信对于 Maven 你已经有了一个初步的了解,但 Maven 除了依赖管理之外提供一系列强大的插件,插件对于 Maven 而言可谓时左膀右臂但却经常被人忽略。
今天就让我介绍一下 Maven 中常用的构建插件。
1. Jar
在使用 Java 开发时通常情况下我们都会将工程打包为 JAR 文件,首先了解一下 JAR 的文件结构。
下图即为通过 Maven 打包后的 JAR 文件,其中 org.example 目录为工程中定义的包名,存在编译后的 .class 文件, META-INF 目录用于存放工程的元数据信息。
如上图中 META-INF 下的 MANIFEST.MF 文件内容如下:
yaml复制代码Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: great
Created-By: Apache Maven 3.6.3
Build-Jdk: 1.8.0_202
而通过 maven-jar-plugin 插件我们即可在添加额外信息至打包后的 JAR 文件,插件配置信息如下:
xml复制代码<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<manifest>
<mainClass>org.example.MyTest</mainClass>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<!-- 配置额外属性信息 -->
<manifestEntries>
<Plugin-Id>demo-plugin</Plugin-Id>
<Plugin-Version>1.0.0</Plugin-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
在之前的工程 POM 文件中添加上述构建插件重新进行打包,可以看到 MANIFEST.MF 文件中即添加了我们配置的额外属性。
yaml复制代码Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: great
Build-Jdk: 1.8.0_202
# Specification entries
Specification-Title: maven-v1
Specification-Version: 1.0-SNAPSHOT
# Implementation entries
Implementation-Title: maven-v1
Implementation-Version: 1.0-SNAPSHOT
Implementation-Vendor-Id: org.example
# Manifest
Main-Class: org.example.MyTest
# ManifestEntries
Plugin-Id: demo-plugin
Plugin-Version: 1.0.0
2. Assembly
在普通 Maven 工程打包时默认仅会编译工程中新建的 java 文件并存储其 .class 文件,对于 POM 文件中引用的第三方依赖并不会一同打包。
如新建一个 Maven 工程并在依赖中导入 Jackson 依赖库并进行打包编译,可以看到下图编译后的 JAR 文件中只有工程中新建的 MyTest.class 文件,项目中所导入的依赖并没有被一起打包。
而通过 assembly 插件即可将 POM 配置中的所有依赖一同打包编译至 JAR 文件中。
其中 execution 标签定义了 assembly 插件的作用阶段,如这里设置了在 Maven package 即打包阶段生效。
xml复制代码<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!-- Set jar file name -->
<finalName>${project.artifactId}-${project.version}-all</finalName>
<appendAssemblyId>false</appendAssemblyId>
<attach>false</attach>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<!-- Set effect phase -->
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
在工程 POM 配置中添加上述信息并重新编译打包工程,可以看到此时 JAR 文件中除了自定义创建的 MyTest.clss 文件外同时包含了依赖的第三方库。
3. Shade
Shade 插件的功能更为强大,其提供了两个功能:第一个即与 assembly 类似可实现依赖的打包编译,与 assembly 不同的是 Shade 提供了更灵活的执行策略,可指定需要打包编译的依赖集合。
另一个即实现包的重命名功能,我们都知道 Maven 并不允许在一共工程中同时引入单个依赖的不同版本,而通过 Shade 插件即可实现二次包装从而绕开该限制。
下面介绍一个 Shade 插件中各标签的使用。
- artifactSet
- 通过 includes 标签可以指定需要一同打包编译的第三方依赖。
- 定义的格式为:groupId:artifactId。
- xml复制代码
- <artifactSet> <includes> <include>groupId:artifactId</include> </includes> </artifactSet>
- relocations
- 通过 relocations 标签即可实现模块的重命名功能。
- 其中 pattern 为需要重命名的模块包, shadedPattern 为重命名后的模块名。
- xml复制代码
- <relocations> <relocation> <pattern>old.package.name</pattern> <shadedPattern>new.package.name</shadedPattern> </relocation> </relocations>
- filters
- 通过 filters 标签可以实现非必要文件的排除,如常见的协议文件等,可通过文件名或类型实现匹配。
- xml复制代码
- <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>filename</exclude> <exclude>file pattern</exclude> </excludes> </filter> </filters>
- 完整配置
- Shade 同样可以通过 execution 设置作用阶段,上述介绍标签的完整配置内容如下:
- xml复制代码
- <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.0</version> <executions> <!-- Working phase --> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> <configuration> <minimizeJar>true</minimizeJar> <!-- Defined what dependencies to pull into the uber JAR --> <artifactSet> <includes> <include>com.fasterxml.jackson.core:jackson-core</include> </includes> </artifactSet> <!-- Rename the package --> <relocations> <relocation> <!-- Old name --> <pattern>com.fasterxml.jackson.core</pattern> <!-- New name --> <shadedPattern>com.ibudai.fasterxml.jackson.core</shadedPattern> </relocation> </relocations> <!-- Exclude the file that didn't want --> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/license/**</exclude> <exclude>META-INF/*</exclude> <exclude>LICENSE</exclude> <exclude>NOTICE</exclude> </excludes> </filter> </filters> </configuration> </plugin> </plugins>
在之前的工程中添加上述配置并重新打包,可以看到编译后的 Jackson 模块包层级已经变成我们自定义的内容,而 Java 的类加载即通过类的完成限定名(包名+类名)来区分是否为同一个类,因此通过 Shade 插件即可实现 Maven 的单一工程多版本引入。
六、构建配置
在上面介绍了工程的依赖管理与多模块的管理配置,下面介绍一下工程打包构建时涉及的配置。
注意以下所有配置项都是定义在 <build> 标签组内,下述不再重复说明。
1. 版本指定
在 <plugin> 标签内可指定工程打包编译时使用的 JDK 版本,可根据服务器环境手动修改版本。
xml复制代码<plugins>
<plugin>
<!-- 编译时使用 JDK 版本 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
2. 文件排除
默认项目打包后 /resources 目录下文件都将统一打包进编译后的 JAR 文件,但为了方便配置修改通常将配置文件排除打包,使用时只需将文件放置于 JAR 同级即可。
如下示例中将 application.yml 文件排除打包,后续若需要修改配置无需重新打包只需重启项目即可。
xml复制代码<resources>
<resource>
<!-- 设置编译去除 yml 配置文件 -->
<directory>src/main/resources</directory>
<excludes>
<exclude>application.yml</exclude>
</excludes>
</resource>
</resources>
3. 主类配置
在打包时可能出现无法识别工程主类的问题,导致编译后的文件无法正常运行,此时则可以在 pom 文件中手动设置工程的主类。
其中 <mainClass> 中配置的为项目主类的完成限定名。
xml复制代码<plugins>
<plugin>
<!-- 设置工程主类路径, 可略去 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>xyz.ibudai.TestWebApplication</mainClass>
<layout>JAR</layout>
</configuration>
</plugin>
</plugins>
作者:烽火戏诸诸诸侯
链接:
https://juejin.cn/post/7266293217054163000
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
相关推荐
- 那些加上姓就很美的名字,想要起名的可以参照一些啊
-
刷到是缘分,感谢大家的阅读,希望您能动动小手帮忙点赞,关注,评论。你的支持和鼓励是我前进的动力。在此祝福大家天天快乐,日日开心!0102我们公司新来的实习生叫林晚,第一次听到这名字就觉得特别有韵味。上...
- 手把手教你写出不被研发怼的需求文档
-
产品经理这岗位都多少年了,我还以为早就体系成熟、流程闭环了,所以这个系列我也安心停更了。结果最近被研发同事轮番吐槽:需求文档东漏一句西漏一段,“这也叫专业?”虽然不是在点名骂我,但谁让是我带的队呢,脸...
- 学会这五个练字规律,解决所有笔画长短问题
-
大家好,用5个规律讲清楚什么时候笔画长,什么时候笔画短。·第一、写的时候是短的笔画多,长的笔画少,记住这一个。·第二、同方向上面只能有一个长,不同方向上面可以同时加长,比如横和竖或者撇捺。这里就有一个...
- 不要再用“质疑”的眼光看草书,5个字说明草书促进汉字简化发展
-
【问题思考】我们知道简体字改革之前,我们用的是繁体字,比如“会”字一直写作“會”,那么是什么依据如是作出简化呢?而在简化字改革前的唐代,我们会发现,孙过庭的《书谱》就是写作“会”的样子的,他是穿越了?...
- IDEA如何将工程转为maven工程
-
有时候在使用IDEA编辑器时,从Git上Pull一个maven工程下来是没有自动将工程转换成maven工程,相应的依赖jar包不会自动下载。此时要将它转换成maven工程并自动下载jar包。方法/步骤...
- Maven多模块项目构建实战:打造高效开发的模块化体系
-
Maven多模块项目构建实战:打造高效开发的模块化体系Maven作为Java世界中最为流行的构建工具之一,以其强大的模块化支持能力著称。当你需要管理一个复杂的大规模项目时,合理使用Maven的多模块功...
- Maven常用命令有哪些?
-
Maven是一个强大的项目管理工具,广泛用于构建、管理和部署Java项目。以下是Maven的一些常用命令,这些命令可以帮助开发者完成项目的清理、编译、测试、打包和部署等任务。所有命令都以m...
- SpringBoot项目jar、war包启动解析
-
一、jar包和war包的区别1.1war包war包是JavaWeb应用程序的一种打包方式符合Servlet标准,它是WebArchive的缩写,主要用于存储Web应用程序相关的文件,包括Java...
- Maven多模块项目构建:打造高效协作的软件工程基石
-
Maven多模块项目构建:打造高效协作的软件工程基石在软件开发的世界里,随着项目的复杂度不断攀升,单一模块的架构逐渐显得力不从心。这时,Maven多模块项目应运而生,它像一座桥梁,将不同的功能模块有机...
- 如何将 Spring Boot 工程打包成独立的可执行 JAR 包
-
导语:通过将SpringBoot项目打包成独立的可执行JAR包,可以方便地在任何支持Java环境的机器上运行项目。本文将详细介绍如何通过Maven构建插件将SpringBoot...
- java maven 工具初步使用
-
安装与配置下载Maven访问官网https://maven.apache.org下载最新版,解压到本地目录(如C:\maven)。配置环境变量添加以下环境变量:M2_HOME:Maven安...
- Maven工程如何使用非Maven仓库jar包
-
使用Maven之前,一直都是自己手工在网上搜索需要的jar包,然后添加到工程中。以这样的方式开发,工作了好多年,曾经以为以后也会一直这样下去。直到碰上Maven,用了第一次,就抛弃老方法了。Maven...
- 从原理和源码梳理Springboot FatJar 的机制
-
一、概述SpringBootFatJar的设计,打破了标准jar的结构,在jar包内携带了其所依赖的jar包,通过jar中的main方法创建自己的类加载器,来识别加载运行其不规...
- Maven初步——Maven的下载、配环境、换源、编译及运行
-
一.maven的基本概念相关定义:Maven是一个项目管理和构建工具,用于构建、发布和管理Java项目,用它的好处很多比如自动帮我们管理包依赖等。1.Maven的四大特性1.Maven引入了一个...
- 「曹工杂谈」Maven和Tomcat能有啥联系呢,都穿打补丁的衣服吗
-
前奏我们上篇文章,跟大家说了下,怎么调试maven插件的代码,注意,是插件的代码。插件,是要让主框架来执行的,主框架是谁呢,就是mavencore,可以称之为maven核心吧。maven核心,类似于...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)