Maven作为Java开发过程中的常用工具软件,在依赖管理和软件构建中发挥了重大作用,为了能够方便工作中的使用,也为了加深对Maven工具的理解,特做系统性的知识收集和汇总总结,以供读者参考学习和使用。
概念
Maven是一个构建工具框架,抽象了构建过程,提供构建任务实现,能帮开发者自动化构建;它跨平台,对外提供了一致的操作接口;它还是一个依赖管理工具和项目管理工具,提供了中央仓库,能帮我们自动下载依赖包。
安装配置
安装
1、确认安装了JDK
2、从Maven官网下载合适版本 依据电脑的操作系统及Maven的版本
3、下载后解压缩到合适位置
4、配置环境变量
如windows在环境变量中添加和修改 M2_HOME 和 PATH
maxOS 中 在.bash_profile 中添加修改 M2_HOME 和 PATH
5、验证版本 在macOS下 mvn -v
Operation not permitted :
试过chmod a+u(或 755) mvn , 不起作用
ls -al@ mvn
xattr -d com.apple.quarantine mvn , 生效
6、IDEA 中配置Maven
Build Tools -> Maven 配置maven home directory \ Settings file \ repository
常用命令
mvn clean :表示运行清理操作(会默认把target文件夹中的数据清理)。
mvn clean compile :表示先运行清理之后运行编译,会将代码编译到target文件夹中。
mvn clean test :运行清理和测试。
mvn clean package :运行清理和打包。
mvn clean install :运行清理和安装,会将打好的包安装到本地仓库中,以便其他的项目可以调用。
mvn clean deploy :运行清理和发布(发布到私服上面)
通过命令来生成模板项目
mvn archetype:generate -DgroupId=org.javaboy -DartifactId=firstapp -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
依赖管理
坐标
<modelVersion>4.0.0</modelVersion>
<groupId>com.chao.home</groupId>
<artifactId>demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo1</name>
- groupId,artifactId和version这三个元素定义了一个项目基本的坐标,在Maven的世界,任何的jar、pom或者jar都是基于这些基本的坐标进行区分的。
- version 项目当前的版本,SNAPSHOT意为快照,处于开发中,是不稳定的。
- name 项目名称
依赖配置
<dependency>
<groupId>实际项目</groupId>
<artifactId>模块</artifactId>
<version>版本</version>
<type>依赖类型</type>
<scope>依赖范围</scope>
<optional>依赖是否可选</optional>
<!—主要用于排除传递性依赖-->
<exclusions>
<exclusion>
<groupId>…</groupId>
<artifactId>…</artifactId>
</exclusion>
</exclusions>
</dependency>
project下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:
- type :依赖的类型,对于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值为jar
- scope :依赖的范围
- optional :标记依赖是否可选
- exclusions :用来排除传递性依赖
依赖范围
依赖范围就是用来控制依赖和三种classpath(编译classpath,测试classpath、运行classpath)的关系,Maven有如下几种依赖范围:
依赖范围 scope | 编译有效 | 测试有效 | 运行时有效 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | – | Y | – | junit |
provided | Y | Y | – | servlet-api |
runtime | – | Y | Y | JDBC驱动实现 |
system | Y | Y | – | 本地的 , maven仓库之外的类库文件 |
- system: 系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致,使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能构成构建的不可移植,因此应该谨慎使用。systemPath元素可以引用环境变量,如:
<dependency>
...
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
- import: 导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。
传递性依赖
spring-boot --> spring-core --> commons-logging
形成了传递性的依赖
依赖范围
假设A依赖B ,B依赖C, A对于C是传递性依赖
最左第一列标识第一直接依赖范围,最上一行标识第二直接依赖范围,中间交叉的表示传递依赖范围
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
依赖冲突
当传递性依赖造成问题的时候,就需要清楚地知道该传递性依赖是从哪条依赖路径引入的。依赖调解有两大原则:
- 路径最近者优先
比如项目有A有这样的依赖关系:A->B->C->X(1.0)、A->D->X(2.0),X是A的传递性依赖,但是两条依赖路径上有两个版本的X,所以根据第一原则,A->D->X(2.0)路径短,所以X(2.0)会被解析使用 - 第一声明者优先
如果路径都一样长的话,先声明的被解析。
仓库
在Maven世界中,任何一个依赖、插件或者项目构建的输出,都可以称为构件。得益于坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。
为了实现重用,项目构建完毕后可生成的构件也可以安装或者部署到仓库中,供其他项目使用。
仓库的布局
任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这便是Maven的仓库布局方式。
该路经与坐标对应关系为groupId/artifactId/version/artifactId-version.packaging。
Maven仓库是基于简单文件系统存储的。
仓库的分类
仓库服务搜索
- Sonatype Nexus:https://repository.sonatype.org/
- MVNrepository:http://mvnrepository.com/
Setting.xml 配置详解
<localRepository>
表示构建系统本地仓库的路径。其默认值:`~/.m2/repository`
<interactiveMode>
表示 `Maven` 是否需要和用户交互以获得输入。
如果`Maven`需要和用户交互以获得输入,则设置成 `true`,反之则应为 `false`。默认为 `true`。
<usePluginRegistry>
`Maven` 是否需要使用`plugin-registry.xml`文件来管理插件版本。
如果需要让 `Maven`使用文件`~/.m2/plugin-registry.xml` 来管理插件版本,则设为 `true`。默认为 `false`。
<offline>
表示 `Maven` 是否需要在离线模式下运行。
如果构建系统需要在离线模式下运行,则为 `true`,默认为 `false`。
当由于网络设置原因或者安全因素,构建服务器不能连接远程仓库的时候,该配置就十分有用。
<pluginGroups>
作用:当插件的组织 id(groupId)没有显式提供时,提供搜寻插件组织 Id(groupId)的列表。默认情况下该列表包含了 org.apache.maven.plugins 和 org.codehaus.mojo。
<servers>
仓库的下载和部署是在pom.xml文件中的 repositories 和 distributionManagement 元素中定义的。配置用户名、密码(有些仓库访问是需要安全认证的)等信息。
<mirrors>
为仓库列表配置的下载镜像列表。
<proxies>
代理配置
<profiles>
作用:根据环境参数来调整构建配置的列表。
它包含了id、activation、repositories、pluginRepositories 和 properties 元素。
如果一个 settings.xml 中的 profile 被激活,它的值会覆盖任何其它定义在 pom.xml 中带有相同 id 的 profile。
<activeProfiles>
自动触发 profile 的条件逻辑。
activeProfile 元素可以包含 profile 的 id。profile 也可以通过在命令行,使用 -P 标记和逗号分隔的列表来显式的激活(如,-P test)。
<activation>
<properties>
<repositories>
<pluginRepositories>
远程仓库的配置
有些依赖存在于特定仓库, 或者为了提高速度 ,可以配置别的仓库
可以在pom.xml中配置该仓库,代码如下:
<!-- 配置远程仓库 -->
<repositories>
<repository>
<id>jboss</id>
<name>JBoss Repository</name>
<url>http://repository.jboss.com/maven2/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
<layout>default</layout>
</repository>
</repositories>
远程仓库认证
仓库权限问题 , 配置在本地setting.xml 中 ,安全考虑
<!--配置远程仓库认证信息-->
<servers>
<server>
<!--这个id将认证信息与仓库配置联系在了一起-->
<id>releases</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>
镜像
提高下载速度或者解决访问不到的问题。
阿里云镜像配置:
// 镜像
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
表示该配置为中央库的镜像,任何对于中央仓库的请求都会转至该镜像
这里介绍下<mirrorOf>
配置的各种选项
*
:匹配所有远程仓库。external:*
:匹配所有远程仓库,使用localhost的除外,使用file://协议的除外。也就是说,匹配所有不在本机上的远程仓库。repo1,repo2
:匹配仓库repo1h和repo2,使用逗号分隔多个远程仓库。*,!repo1
:匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中排除。
代理
有时候基于公司的安全管控,或网络因素 ,访问不到默认的公共仓库 如 repo1.maven.org , 可以通过代理来访问 , 在使用代理时序检查代理服务是否连通正常 , telnet xxxx xx
// 代理可以配置多个 默认情况下 第一个被激活生效
// 当代理服务需要认证时 需要配置 username password ,
nonProxyHost 用来指定哪些主机不需要代理 可用用 ‘|’ 符号来分隔多个主机名, 可以支撑通配符
// 代理配置
<proxies>
<proxy>
<id>my-proxy</id>
<active>true</active>
<protocol>http</protocol>
<host>xxxx</host>
<port>3128</port>
<!--
<username>***</username>
<password>***</password>
<nonProxyHosts>
repository.mycom.com|*.google.com </nonProxyHosts>
-->
</proxy>
</proxies>
pom.xml文件dependencies模块的配置
在理解了依赖引用后 ,对于公众的依赖配置就比较好懂了, 这时候配置要结合着父级工程的依赖和dependencyManagement 来合理配置依赖, 版本管理则结合properties 项来集中管理版本号
<project >
<parent>
<groupId>com.xx.staff.chao</groupId>
<artifactId>practice</artifactId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.xx.staff.chao</groupId>
<artifactId>demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo1</name>
<!-- ### 归类依赖 -->
<properties>
<custom.version>1.5.6</custom.version>
</properties>
<dependencyManagement>
....
</dependencyManagement>
<dependencies>
<dependency>
……
<version>${custom.version}</version>
<!-- ### 排除依赖 -->
<exclusions>
<exclusion>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
聚合工程
用于多模块项目的管理
|--javaboy-parent
|-- javaboy-cms
|-- javaboy-crm
|-- javaboy-manger
|-- javaboy-manager-model
|-- javaboy-manager-dao
|-- javaboy-manager-service
|-- javaboy-manager-web
idea 中创建多模块工程
创建个maven 工程后 删除 src
选择当前工程 添加模块 New -> Module
部署构建至远程仓库
将项目生成的构件部署到远程仓库中需要配置distributionManagement元素,代码如下:
<distributionManagement>
<repository>
<id>releases</id>
<name>public</name>
<url>http://xxxx/nexus/content/repositories/releases</url>
</repository>
<snapshotRepository>
...
</snapshotRepository>
</distributionManagement>
常见pom中属性
${project.build.sourceDirectory}:项目的主源码目录,默认为src/main/java/.
${project.build.testSourceDirectory}:项目的测试源码目录,默认为/src/test/java/.
${project.build.directory}:项目构建输出目录,默认为target/.
${project.build.outputDirectory}:项目主代码编译输出目录,默认为target/classes/.
${project.build.testOutputDirectory}:项目测试代码编译输出目录,默认为target/testclasses/.
${project.groupId}: 项目的groupId.
${project.artifactId}: 项目的artifactId.
${project.version}: 项目的version,于${version}等价
${project.build.finalName}: 项目打包输出文件的名称,默认:${project.artifactId}${project.version}.
${basedir} 项目根目录
${project.packaging} 打包类型,缺省为jar
${project.xxx} 当前pom文件的名为xxx节点内容
构建管理
Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成。
用户可以通过两种方式调用插件目标:第一种方式是将插件目标与生命周期阶段(lifecycle phase)绑定;第二种方式是直接在命令行指定要执行的插件目标。
插件
插件主要分两类 构建类插件和报告类插件 , 具体插件可在如下的官网地址中查阅:
https://maven.apache.org/plugins/index.html
maven- compiler-plugin
编译源代码, maven- compiler-plugin的compile目标用来编译位于src/main/java/目录下的主源码,testCompile目标用来编译位于src/test/java/目录下的测试源码。
maven-jar-plugin
仅负责将源码打成jar包,不能独立运行。可以根据设置,将依赖jar包路径和程序的主入口定义在所打jar包中的MANIFEST.MF 文件里。常见配置如下:
<configuration>
<!-- 打包时包含的文件配置,只打包 com 文件夹 -->
<includes>
<include>
**/com/**
</include>
</includes>
<archive>
<manifest>
<!-- 配置加入依赖包 -->
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<useUniqueVersions>false</useUniqueVersions>
<!-- Spring Boot 启动类(自行修改) -->
<mainClass>com.chao.MyApplication</mainClass>
</manifest>
<manifestEntries>
<!-- 外部资源路径加入 manifest.mf 的 Class-Path -->
<Class-Path>resources/</Class-Path>
</manifestEntries>
</archive>
<!-- jar 输出目录 -->
<outputDirectory>${project.build.directory}/pack/</outputDirectory>
</configuration>
maven-dependency-plugin
负责将各种依赖打包,也可以根据你的设置,将所打的依赖jar包输出到指定位置。它最大的用途是帮助分析项目依赖。
常见配置如下:
<!-- 复制依赖 -->
<executions>
<execution>
<!-- 这里的id可以随意写,与下面的goal无名称上的必然联系 -->
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!-- 依赖包 输出目录 -->
<outputDirectory>${project.build.directory}/pack/lib</outputDirectory>
</configuration>
</execution>
</executions>
1. copy:拷贝指定jar包到指定目录,与当前工程的依赖没有关系
2. copy-dependencies:拷贝依赖jar包到指定目录
3. unpack:解压指定jar包到指定目录,与当前工程的依赖没有关系
4. unpack-dependencies:解压依赖jar包到指定目录
maven-resourcee-plugin
负责将正式与测试用到的资源文件导出到指定位置。还负责用资源文件中的参数替换pom文件中对应的占位符,常用配置如下:
<!-- 在打包时,动态将maven的参数传给resource文件夹下的参数 -->
<configuration>
<encoding>UTF-8</encoding>
<!-- 当这里为true,那么resource文件夹下的配置文件,比如application.yml这些文件里面的${}包起来的内容就可以被pom文件中profiles标签下的对应名称部分行替换了 -->
<useDefaultDelimiters>true</useDefaultDelimiters>
</configuration>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<!-- 文件来源 -->
<directory>src/main/resources</directory>
</resource>
</resources>
<outputDirectory>${project.build.directory}/pack/resources</outputDirectory>
</configuration>
maven-assembly-plugin
负责打包, 用途是制作项目分发包,该分发包可能包含了项目的可执行文件、源代码、readme、平台脚本等等。 支持的格式如zip、tar.gz、jar和war等,具体打包哪些文件是可配置的。是基于 jar-plugin 和 dependency-plugin 的成果做了二次加工。它会把其他插件打出的jar包以及用户指定的额外项目也打入到成品之中。
spring-boot-maven-plugin
负责将源码和依赖,保证打出的包能独立运行, springboot二次封装的。相当于 jar-plugin 和 dependency-plugin 的功能合成,会把springboot的一些个性化的内容打到包里。比如 springboot loader 内容。
maven-shade-plugin
用来打可执行JAR包,也就是所谓的 fat jar包
maven-surefire-plugin
负责测试,这一插件可以在maven的test阶段单独执行,在编译打包阶段可以被执行或跳过。可以指定其测试的范围或全量执行所有以Test开头或结尾的代码 。
例如 mvn test -Dtest=FooTest 是通过控制maven-surefire-plugin的test参数实现的。
maven-help-plugin
maven-help-plugin是一个小巧的辅助工具,最简单的help:system可以打印所有可用的环境变量和Java系统属性。help:effective-pom和help:effective-settings最为有用,它们分别打印项目的有效POM和有效settings。
jetty-maven-plugin
jetty-maven-plugin能够周期性地检查源文件,一旦发现变更后自动更新到内置的Jetty Web容器中。
总结
maven 是依赖管理和构建管理的工具
解决依赖的问题 :
标识 、仓库 、 复用
仓库 、自动下载 、自动解决依赖问题
解决构建问题 :
项目生命周期 编译、测试、打包、部署、上传到私服等
用插件来解决各场景的构建 和 报告