如果你是有一定的开发经验,我相信你一定被项目 lib 下的 JAR 包折磨过,如果碰上兼容问题,更是逐个下载不同版本 JAR 包进行替换排查,相信是每个程序员都不想再经历一边的噩梦。
Maven 的出现则大大降低开发人员的准备工作,让开发人员更专心与业务,下面即介绍 Maven 基本使用。
Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。
一、基础配置1. 仓库配置
在 Maven 中引入了仓库的概念,开发人员将所编写的 JAR 按照相应格式推送到仓库中,当其他开发者需要引用这个 jar 包时在工程中引用相应依赖,则会先从中央仓库进行下载到本地仓库,此时项目将读取本地仓库的内容。
对于部分组织或机构通常会在此基础上额外搭建私人仓库,在引用依赖时会先从私人仓库进行读取,如果未找到再从中央仓库下载至私人仓库,最后再下载到本地仓库。
通过这种方式开发者则无需再手动管理繁杂的项目 JAR 包,从而实现更高的效率。
2. 基本信息
一个最基本的 Maven 项目通常应包含如下内容,当我们引用一个模块时,也是通过groupId、artifactId、version三项内容进行确定。
下面是一个基本定义示例:
<project ...>
<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标签我们即可导入所需要的工程依赖。
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
<scope>runtime</scope>
</dependency>
</dependencies>其中scope的可选值如下:
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排除依赖。
<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作为间接依赖导入。
<dependencies>
<dependency>
<groupId>xyz.ibudai</groupId>
<artifactId>demo-b</artifactId>
<version>1.0.0</version>
<optional>true</optional>
</dependency>
</dependencies>4. 变量配置
当项目中引入了大量依赖,为了方便管理通常将引入依赖的版本通过变量进行统一配置,从而实现更直观的依赖管理。
通过properties标签即可自定义变量配置,然后使用${}引用变量。
<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标签即可实现模块管理。
<modules>
<module>module-1</module>
<module>module-2</module>
</modules>如下在maven-demo中又包含了module-1和module-2两个工程。
2. 模块继承
通过parent即可标记当前模块的父模块,且子模块将会继承父模块中的所有依赖配置。子模块若没有指定的groupId和version默认继承父模块中的配置。
其中relativePath用于指定父模块的 POM 文件目录,省略时默认值为../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两个依赖。
<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 文件内容如下:
<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我们即实现了项目依赖版本的统一管理。
<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。
通过导入即可实现更轻量化的模块信息继承,具体配置内容如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>xyz.ibudai</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>import</scope>
<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文件内容如下:
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 文件,插件配置信息如下:
<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文件中即添加了我们配置的额外属性。
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.02. Assembly
在普通 Maven 工程打包时默认仅会编译工程中新建的 java 文件并存储其.class文件,对于POM文件中引用的第三方依赖并不会一同打包。
如新建一个 Maven 工程并在依赖中导入Jackson依赖库并进行打包编译,可以看到下图编译后的 JAR 文件中只有工程中新建的MyTest.class文件,项目中所导入的依赖并没有被一起打包。
而通过assembly插件即可将 POM 配置中的所有依赖一同打包编译至 JAR 文件中。
其中execution标签定义了assembly插件的作用阶段,如这里设置了在Maven package即打包阶段生效。
<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>
<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>
<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。
<artifactSet>
<includes>
<include>groupId:artifactId</include>
</includes>
</artifactSet>relocations
通过relocations标签即可实现模块的重命名功能。
其中pattern为需要重命名的模块包,shadedPattern为重命名后的模块名。
<relocations>
<relocation>
<pattern>old.package.name</pattern>
<shadedPattern>new.package.name</shadedPattern>
</relocation>
</relocations>filters
通过filters标签可以实现非必要文件的排除,如常见的协议文件等,可通过文件名或类型实现匹配。
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>filename</exclude>
<exclude>file pattern</exclude>
</excludes>
</filter>
</filters>完整配置
Shade 同样可以通过execution设置作用阶段,上述介绍标签的完整配置内容如下:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<minimizeJar>true</minimizeJar>
<artifactSet>
<includes>
<include>com.fasterxml.jackson.core:jackson-core</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>com.fasterxml.jackson.core</pattern>
<shadedPattern>com.ibudai.fasterxml.jackson.core</shadedPattern>
</relocation>
</relocations>
<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 的单一工程多版本引入。
六、构建配置
在上面介绍了工程的依赖管理与多模块的管理配置,下面介绍一下工程打包构建时涉及的配置。
注意以下所有配置项都是定义在标签组内,下述不再重复说明。
1. 版本指定
在
标签内可指定工程打包编译时使用的 JDK 版本,可根据服务器环境手动修改版本。
<plugins>
<plugin>
<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文件排除打包,后续若需要修改配置无需重新打包只需重启项目即可。
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>application.yml</exclude>
</excludes>
</resource>
</resources>3. 主类配置
在打包时可能出现无法识别工程主类的问题,导致编译后的文件无法正常运行,此时则可以在 pom 文件中手动设置工程的主类。
其中中配置的为项目主类的完成限定名。
<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>来源:/post/726629321705416300
·················END·················
你好,我是小孟,10年开发老司机,小作坊boss、做过码农、主管、产品经理。喜欢自由,讨厌职场的勾心斗角。我的偶像是乔布斯,10年前选择计算机这个行业,就是因为热爱。现在已而立之年,虽然没有当初追寻技术的单纯,但依然热爱。技术改变世界,知识改变命运是我不变的信念。学习、思考、因时代变化而变化是我的武器。如果我觉得一件事,值得干,至少我会坚持5年。在我毕业一年后,我就搞定了房贷、车贷等一切贷款,所以我可以自由。我不喜欢循规蹈矩和被安排的生活,大学时候就喜欢折腾,现在也是。我相信坚持的力量,如果能把一件事坚持下去,真的可以打败99%的人。关注我,一起聊技术、职场、人生和好玩的事。
你对的起时间,时间也会对的你。一个人可以走的很快,一群人走的很远。开发项目,
限时特惠:本站每日持续更新海内外内部创业教程,一年会员只需88元,全站资源免费下载点击查看详情。
站长微信:nnxmw123声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。