史上最全Maven教程,没有之一(逃😎)

内容分享1个月前发布 DunLing
1 0 0

如果你是有必定的开发经验,我信任你必定被项目 lib 下的 JAR 包折磨过,如果碰上兼容问题,更是逐个下载不同版本 JAR 包进行替换排查,信任是每个程序员都不想再经历一边的噩梦。

Maven 的出现则大大降低开发人员的准备工作,让开发人员更专心与业务,下面即介绍 Maven 基本使用。

Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。

一、基础配置

1. 仓库配置

在 Maven 中引入了仓库的概念,开发人员将所编写的 JAR 按照相应格式推送到仓库中,当其他开发者需要引用这个 jar 包时在工程中引用相应依赖,则会先从中央仓库进行下载到本地仓库,此时项目将读取本地仓库的内容。

对于部分组织或机构一般会在此基础上额外搭建私人仓库,在引用依赖时会先从私人仓库进行读取,如果未找到再从中央仓库下载至私人仓库,最后再下载到本地仓库。

史上最全Maven教程,没有之一(逃😎)

通过这种方式开发者则无需再手动管理繁杂的项目 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教程,没有之一(逃😎)

  • 依赖顺序
  • 在 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 版本。

史上最全Maven教程,没有之一(逃😎)

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 两个工程。

史上最全Maven教程,没有之一(逃😎)

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教程,没有之一(逃😎)

  • 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 目录用于存放工程的元数据信息。

史上最全Maven教程,没有之一(逃😎)

如上图中 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 文件,项目中所导入的依赖并没有被一起打包。

史上最全Maven教程,没有之一(逃😎)

而通过 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 文件外同时包含了依赖的第三方库。

史上最全Maven教程,没有之一(逃😎)

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

来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...