xl_echo编辑整理

百战不败,依不自称常胜,百败不颓,依能奋力前行。——这才是真正的堪称强大!!!

maven

maven是一个跨平台的项目管理的工具。隶属于Apache下的一个开源项目。主要服务于Java平台的项目构建、依赖管理、项目信息管理等。

0、前言

在开发java项目的时候,项目编译、测试、运行、打包等等都有着极高的成本,跨部门甚至跨人员之间的项目结构都有可能不一样,除此之外跨部门甚至跨人员之间的项目结构都有可能不一样,这使得成本加倍。maven就是为了解决这些问题的,在跨平台(Linux、Windows、Mac)的基础上,使用Archetype完成各种项目统一的骨架,还进行标准化的构建过程,包含了包版本管理、库依赖、集成测试、编译、安装、运行等。

接下来,将分别认识和学习setting.xml、仓库、生命周期、插件等模块。(注:本文所有的路径都是在Mac下面的,其他平台类似)

1、setting.xml

setting.xml 是maven的管理配置文件,包含了系统级别的配置和当前用户级别的配置,用户级别的路径是~/.m2,系统级别的是 M2HOME/conf,当然了 M 2 H O M E / c o n f , 当 然 了 M2_HOME是在安装的时候设置好的maven路径,我们一般使用的是用户级别的文件。

该文件可以配置仓库、代理、profile、镜像、插件等,更多更详细的配置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>

localRepository

本地仓库地址,默认情况下,下载到本地的代码库存放在${user.home}/.m2/repository文件夹中的,用户如果想存在放其他地方,配置该属性即可

interactiveMode

用户交互模式,默认为true

usePluginRegistry

是否使用插件仓库、默认为false、如果需要使用,则需要在当前路径下创建plugin-registry.xml文件并设置为true

offline

是否在离线状态下运行,如果系统需要在离线状态下运行,则设置为true,默认为false

pluginGroups

插件组,当插件的groupId没有显式提供时,供搜寻插件(groupId)的列表

<pluginGroups>
<pluginGroup>org.mortbay.jetty</pluginGroup>
</pluginGroups>

入上述代码,包含了org.mortbay.jetty 的插件,例如,当我们需要执行该插件的run目标时,可以执行org.mortbay.jetty:jetty-maven-plugin:run,又或者可以简化成mvn jetty:run,关于为何会简化成如此,到插件的部分细说。

servers

服务配置,主要是针对需要鉴权的仓库的配置,有些仓库默认匿名用户可以访问,但是可能存在一些私服需要对用户进行鉴权,有相关权限的用户才可以继续访问或者操作。

<servers>
<server>
<id>server001</id> <!-- 服务ID,和仓库ID关联 -->
<username>my_login</username> <!-- 用户名 -->
<password>my_password</password> <!-- 密码 -->
<privateKey>${user.home}/.ssh/id_dsa</privateKey> <!-- 私钥路径 -->
<passphrase>some_passphrase</passphrase> <!-- 私钥密码 -->
<filePermissions>664</filePermissions> <!-- 文件被创建权限-->
<directoryPermissions>775</directoryPermissions> <!-- 文件夹被创建权限 -->
<configuration></configuration>
</server>
</servers>

mirrors

镜像地址,对仓库地址的一种映射关系,国外的仓库地址可能不是很稳定,类似于AliBaBa和OSchina搭建的镜像地址,我们可以配置镜像地址,使得访问速度更快

<mirrors>
<mirror>
<id>planetmirror.com</id> <!-- 镜像ID,唯一性即可-->
<name>PlanetMirror Australia</name> <!-- 镜像名字 -->
<url>http://downloads.planetmirror.com/pub/maven2</url> <!-- 镜像地址 -->
<mirrorOf>central</mirrorOf> <!-- 映射的具体仓库ID以及其操作-->
</mirror>
</mirrors>

没有镜像的时候

需要直连A仓库,如果A仓库在国外可能存在各种问题不稳定工作

Java项目管理工具Maven详解_Java

image.png

有镜像的时候

这时候存在一个B镜像,可以间接的访问到国外不稳定的A仓库,那我们就可以去加入B镜像就可以了。

Java项目管理工具Maven详解_Maven详解_02

image.png

需要注意下mirrorOf,这个参数有多种配置,而且是针对仓库ID过滤的

  • 表示所有
    external:* = 所有不在本地、不适用file://协议的
    repo,repo1 = repo和repo1仓库
    *,!repo1 = 所有的仓库,除了repo1

更多细节可看官方文档===>Guide to Mirror Settings

  • proxies
    代理,主要是为了便于在各自网络环境下使用
<proxies>
<proxy>
<id>myproxy</id> <!-- 代理ID,唯一性即可-->
<active>true</active> <!-- 是否激活,激活了就可以使用该代理-->
<protocol>http</protocol>
<host>proxy.somewhere.com</host>
<port>8080</port> <!-- 协议、主机、端口-->
<username>proxyuser</username>
<password>somepassword</password>
<nonProxyHosts>*.google.com|ibiblio.org</nonProxyHosts> <!-- 不代理的主机,中间用竖划线区分-->
</proxy>
</proxies>

profiles 和 activeProfiles

profile,拥有多套环境,可以根据参数任意切换,如需使用需要在activeProfiles中加入该profile的ID。

包含了activation, repositories, pluginRepositories和properties四种元素,其中activation就类似于properties,就和一个KV对一般,剩下的就是仓库和插件了

Repositories

仓库的配置,其中包含了快照版本SNAPSHOT和发布版本RELEASE

<repositories>
<repository>
<id>codehausSnapshots</id> <!-- 仓库ID 需唯一 -->
<name>Codehaus Snapshots</name> <!-- 仓库名称 -->
<releases> <!-- 正式版本一-->
<enabled>false</enabled> <!-- 是否可正常使用 -->
<updatePolicy>always</updatePolicy> <!-- 更新策略 -->
<checksumPolicy>warn</checksumPolicy> <!-- 校验策略,分为ignore、fail、warn -->
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
<url>http://snapshots.maven.codehaus.org/maven2</url>
<!-- 仓库地址,按protocol://hostname/path形式 -->
<layout>default</layout> <!-- -->
</repository>
</repositories>

如上代码,需要注意 updatePolicy 更新策略,主要是如下几种值


  • always 总是更新
  • daily 每天更新一次(默认值)
  • interval:X X分钟更新一次
  • never 永远都不更新

2、仓库

什么叫仓库呢?顾名思义就是存放物品的地方,在Java项目中肯定就是存放了各种各样的jar包,避免了每开发一个项目都要导入各种各样的jar包,全部统一存放在仓库中,使用的时候只需要在POM中声明所需要的包的GroupId和artifactId,在使用的时候会自动关联相关jar包。如下图是仓库的分类图

Java项目管理工具Maven详解_Java_03

image.png

刚刚安装maven之后,是没有本地仓库的,必须得有一个可用的远程仓库,这样才可以把需要的构建下载到本地来的

Java项目管理工具Maven详解_Maven详解_04

image.png

私服是一种特殊的远程仓库,搭建在局域网内的仓库,私服代理广域网的仓库,提供给局域网内的用户使用,可用减少局域网内的用户同外界仓库的传输,每一个jar包只需要拉取一次就可以提供给局域网内所有的用户使用,并且也更加稳定

减少网络带宽流量

加速Maven构建

部署第三方构件

提高稳定性、增强控制

降低中央仓库的负载

部署到远程仓库

上面所说的仓库和我们现在说的远程仓库是同一种东西,只是一个是推送到远程,另一个是拉取到本地使用。

部署到远程仓库,需要在各自项目的POM文件中部署好distributionManagement元素,例如如下代码

<distributionManagement>
<repository>
<id>deployment</id>
<name>internal repository for releases</name>
<url>http://test.com/nexus/releases/</url>
</repository>
<snapshotRepository>
<id>deployment</id>
<name>internal repository for snapshots</name>
<url>http://test.com/nexus/snapshots/</url>
</snapshotRepository>
</distributionManagement>

配置中包含了release和snapshot两种版本模式,前者表示稳定版本,一般情况下提供给别人活着发布到生产环境的都是该版本,snapshot快照版本则是处于开发模式。而且如果当前项目是快照版本则部署到快照仓库,否则就部署到发布仓库。

在部署到远程仓库需要用户认证的时候,就需要设置在setting.xml的service元素了,在service中的ID必须和仓库的ID保持一致。

部署是通过maven-deploy-plugin的deploy功能完成部署操作的,插件到后面细说。

3、坐标和依赖

maven的坐标预定了世界上任何一个构建的位置,只需要约定好正确的坐标,就可以正确的找到所需的构建,坐标元素包括groupId,artifactId, version等。

坐标详解


  • groupId (必选)定义当前maven项目隶属的实际项目
  • artifactId (必选)实际项目中的一个maven模块
  • version (必选)版本号
  • packaging (可选)打包方式,常用的分为jar和war包,插件则是maven-plugin,默认为jar包
  • classifier (不可直接定义)帮助定义构建出的一些附属构建,例如source和对应的doc

依赖配置

依赖配置是在pom文件中的配置的元素,在dependencies中的,可以包含一个或者多个dependency元素,每个依赖包含如下元素


  • groupId、artifactId、version 依赖的基础坐标,最重要的
  • type 依赖的类型,对应到坐标的packaging,默认情况下不必声明,其值为jar
  • scope 依赖的范围,不同的依赖会使用不同的classpath
  • compile 编译依赖的范围,没有指定则默认使用该范围
  • test 测试,只对测试的classpath有效
  • provided 已提供依赖范围,在编译和测试时有效,但是在运行时无效
  • runtime 运行时的依赖范围
  • system 系统依赖范围,和本机系统绑定
  • import 导入依赖范围
  • optional 依赖是否为可选,确认依赖是否被继承
  • exclusions 排除传递性依赖

依赖调解

传递性依赖机制能够大大的简化依赖声明,而且大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递性依赖,但是当出现冲突了,则需要很清楚依赖性依赖是从什么依赖路径引入的。

例如A项目有这样的依赖关系A->B->C->X(1.0),A->B->X(2.0),那么哪个X版本会被引用呢?

第一原则:最短路径优先,上述例子中会使用X2.0版本,但是这并不能解决所有的问题,例如又有如下的依赖关系A->B->D(1.0),A->B->D(2.0),使用最短路径则出现的了不确定性

第二原则:第一声明者优先,这个例子中,如果D1.0版本在pom中更加靠前,则会使用D1.0版本

可选依赖

确认依赖是否被继承,如下图所示,如果B有optional元素的依赖则表示该元素是可选依赖,从A出发的依赖传递性就被打破了,A无法依赖X,Y了,一般情况下,也不怎么使用该情况

依赖查看

依赖可以通过dependency插件查看更多信息,例如mvn dependency:tree查看最后的依赖结果


  • dependency:list 展示所有的依赖情况
  • dependency:tree 树形结构展示依赖情况
  • dependency:analyze 分析依赖树

4、生命周期和插件

软件开发的整个过程也是有生命周期的,开发、编译、测试以及部署,而maven的生命周期就是为了所有对象构建过程进行的抽象和统一。maven的生命周期包含了项目的清理、初始化、编译、测试、打包以及集成测试,和最后的验证和部署操作。maven的生命周期本身是抽象的,不参与任何具体的工作,只是调用各种插件完成所需的任务。例如常见的插件有


  • maven-compiler-plugin 编译使用的插件
  • maven-surefire-plugin 测试使用的插件
  • maven-deploy-plugin 部署使用的插件

5、聚合和继承

此小节主要是涉及到多模块下的各个POM文件的关系。一个项目中如果存在多个模块,就会使得一个父POM挂着多个子POM。

如果实际开发中,只是单独开发子模块,并不关心父模块,所以在引用父模块的时候需要指定父POM的路径,使用relativePath关键字,如下图所示

因为存在父亲模块,所以子模块只需要继承父POM引入的各种jar包,无需再次引入,类似于springboot、junit等模块,存在父POM即可。

不过,同时又有另一种情况的存在,例如新加入的子模块,无需引入上述模块,应该如何处理呢?

POM中的dependencyManagement元素既可以让子模块继承父模块的配置,也可以保证子模块的灵活。

父亲模块

<properties>
<spring.version>4.3.9.RELEASE</spring.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

子模块

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
</dependencies>

做一个有底线的博客主