1 概念和原理
1.1 POM文件的基本元素
modelVersion:指定POM模型的版本,对Maven2、Maven3来说只能是4.0.0。
groupId:定义Maven项目(模块)隶属的实际项目,即项目属于那个组,主要来标志项目,格式为:com+公司名+项目名,例如”com.mycom.myapp”。
artifactId:定义实际项目的Maven项目(模块),即项目的模块名称,主要是来标志模块,例如“util”、“implementation”,如果模块可以分为子模块,那么可以用“-”或者“.”分开,比如“util.io”,“util.log”或者“util-io”,“util-log”。有些情况下也在artifactId命名以groupId作为前缀,比如myapp-utli,这样有区别于其他项目的util-XXX-util。
version:项目当前的版本,这个要和modelVersion区分。
packaging:定义打包类型,jar或者war,默认是jar。:
name:声明的对用户更友好的名称,有助于交流信息,这个不是必须的属性,但是这个属性会在Maven执行过程的打印信息中作为模块的描述,所以建议写全。
classfier:该语速用来半数定义构建输出的一些附属构件,这个属性不能直接定义。
注意:
1、modelVersion、groupId、packaging、version、classfier定义了一个项目的基本坐标,在Maven世界,任何的jar、pom或者war都是以基于这些基本的坐标进行区分的。一般jar文件的命令格式:groupId+ artifactId+ “-”+version +“.jjar”
2、在Maven世界中,任何一个依赖(依赖的jar包)、插件(各种功能插件)或者项目输出(目标插件或者目标jar等)都可以称为构件。
1.2 依赖
1.2.1 依赖范围
依赖范围就是用来控制依赖与编译classpath、测试classpath、运行classpath这三种classpath的关系。
compile:编译依赖范围,使用这种依赖,对于编译、测试、运行三种classpath都有效。如果没有指定,默认就是这种依赖。典型的例子是spring-core,编译、测试和运行都需要使用该依赖。
test:测试依赖范围,使用这种依赖,只对测试classpath有效,在编译住代码或者运行项目时无法使用此依赖。典型的例子是JUnit,,它只有在编译测试代码以及运行测试的时候才需要。
provided:已提供的依赖方位。使用这种依赖,对于编译和测试的classpath有效,但是运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要依赖,但是运行的时候,由于容器已经提供,就不需要Maven重复地引入一遍。
runtime:运行时依赖。这种依赖对于测试和运行的classpath有效,但是编译猪代码时无效。典型的例子是JDBC驱动实现,项目猪代码的编译需要JDK提供的JDBC接口。只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
system:系统依赖范围。该依赖与但中classpath的关系和provided依赖范围完全一直。但是,使用system范围的依赖时必须通过systemPaht元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构件的不可以移植,因此应该谨慎使用。
import(Maven2.0.9及以上):导入依赖范围。该依赖类型不会对三种classpath产生实际影响。在依赖管理配置实现导入现有POM中配置的依赖管理。
1.2.2 依赖配置
可以在POM或者setting.xml中配置,具体见常见配置。
1.3 仓库
1.3.1 仓库分类
本地仓库:本地库,查找依赖和构件都基于该仓库。如果该仓库没有,从远程仓库下载到本地仓库,再供使用。默认的中央仓库位置在$M2_HOME/lib/maven-model-builder-3.0.jar的POM文件中已经定义。
远程仓库-中央仓库:中央仓库是Maven核心自带的远程仓库,它包含了大部分开源的构建。
远程仓库-私服:特殊的远程仓库,代理所有外部的远程仓库,提供内部项目的构建给其他项目使用。
私服的作用:节省带宽、加速Maven构建、部署第三方构件、提高稳定性,增强控制、降低中央仓库的负荷。
远程仓库-其他公共库:比如JAVA.NET,JBOSS等
1.3.2 从仓库解析依赖的机制
1.3.3 镜像
如果仓库X可以提供仓库Y的所有内容,那么就可以认为X是Y的一个镜像。换句话说,任何一个可以从仓库Y获得的构件,都可以从它的镜像中获取。实际中可以配置中央仓库的镜像、配置所有仓库的镜像—私服(私服就是所有仓库的镜像)。
1.4 生命周期和插件
三套生命周期:clean、default、site,每套生命周期包括若干阶段,每个阶段依赖于前面的阶段。各个生命周期独立。
命令行与生命周期:命令行执行maven任务的主要方式是调用Maven生命周期的阶段。
生命周期和插件:插件实现生命周期各个阶段的具体任务,生命周期和插件进行了内置的绑定,但是通过修改配置也能替换内置的插件(修改绑定)或者增加默认(内置)没有绑定的绑定关系(自定义绑定)。为了复用代码,一个插件往往能完成多个任务,每个功能称作插件的一个目标。完成了插件和生命周期的绑定之后,用户还可以配置插件目标的参数,进一步调整插件目标所执行的任务,以满足项目的需求。几乎所有Maven插件的目标都有一些可配置的参数,用户可以通过命令行和POM配置的方式来配置这些参数。
1.4.1 常用Maven命令和执行的任务(目标—对插件而言、阶段—对生命周期而言)
clean:clean mvn clean
resources:resources-主资源处理
compiler:compile-主代码编译 mvn compile
resources:restResources-测试资源处理
compiler:testCompile-测试代码编译
surfire:test-测试 mvn test
jar:jar-打包 mvn package
install:install-安装(到本地repository) mvn install
1.4.2 插件
1.4.2.1 插件仓库
与依赖构件一样,插件构件同样基于坐标存在Maven仓库中。在需要的时候,Maven会从本地仓库寻找插件,如果不存在,则从远程仓库查找。找到插件之后,再下载到本地仓库使用。Maven会区别对待依赖的远程仓库与插件的远程仓库,因此依赖的远程仓库和插件的远程仓库是分别配置的,前者用<repositories >标签,而后者用<pluginRepository>标签,标签下的配置项目含义一致,同样可以在POM或者setting.xml中配置。
1.4.2.2 插件解析
原理和依赖解析一致。但是插件有前缀的概念,即执行命令的时候可以省略groupId,省略的时候默认去org.maven和org.codehause中去解析,即默认会使用org.maven.maven.plugins和org.codehause.mojo这两个gourpId。也可以通过setting.xml让Maven检查其他groupId上的插件仓库元数据。
<settings>
<pluginGroups>
<pluginGroup>com.your.plugins<pluginGroup>
<pluginGroups>
<settings>
基于上面的配置,Maven就不仅仅检查org/apache/maven/plugins/maven-metadata.xml和org/codehaus/mojo/ maven-metadata.xml,还会检查org/com/your//plugins/maven-metadata.xml。
1.5 聚合与继承
1.5.1聚合
Maven聚合(或者称为多模块)提供多POM模块(项目)的统一处理机制。
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-aggregator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging> pom </packaging>
<name>Account Aggregator</name>
<modules>
<module>account-email</module>
<module>account-persist</module>
</modules>
</project>
packaging的类型为pom ,module的值是一个以当前POM为主目录的相对路径。
1.5.2 继承
Maven继承是为了消除重复,简化配置等提供的机制。
父模块
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId> account-parent </artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Parent</name>
</project>
子模块声明继承
<project>
<modelVersion>4.0.0</modelVersion>
< parent >
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId> account-parent </artifactId>
<version>1.0.0-SNAPSHOT</version>
< relativePath >../account-parent/pom.xml</ relativePath>
</ parent >
<artifactId> account-email </artifactId>
<name>Account Email</name>
...
</project>
最后,同样还需要把 account-parent加入到聚合模块account-aggregator中。聚合的 POM
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-aggregator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging> pom </packaging>
<name>Account Aggregator</name>
<modules>
<module>account-email</module>
<module>account-persist</module>
<module> account-parent</module>
</modules>
</project>
注意:
1、子模块没有声明groupId和version, 这两个属性继承至父模块。但如果子模块有不同与父模块的 groupId、version ,也可指定;
2、不应该继承artifactId,如果groupId ,version,artifactId 完全继承的话会造成坐标冲突;另外即使使用不同的 groupId或version,同样的 artifactId也容易产生混淆。
3、使用继承后 parent也必须像自模块一样加入到聚合模块中。也就是在在聚合模块的 pom中加入<module>account-parent</module>
1.5.2.1 可继承的POM 元素
groupId :项目组 ID,项目坐标的核心元素;
version :项目版本,项目坐标的核心元素;
description :项目的描述信息;
organization :项目的组织信息;
inceptionYear :项目的创始年份;
url :项目的 url地址
develoers :项目的开发者信息;
contributors :项目的贡献者信息;
distributionManagerment:项目的部署信息;
issueManagement :缺陷跟踪系统信息;
ciManagement :项目的持续继承信息;
scm :项目的版本控制信息;
mailingListserv :项目的邮件列表信息;
properties :自定义的 Maven属性;
dependencies :项目的依赖配置;
dependencyManagement:醒目的依赖管理配置;
repositories :项目的仓库配置;
build :包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等;
reporting :包括项目的报告输出目录配置、报告插件配置等。
1.5.2.2 依赖管理
Maven提供的dependencyManagement元素,既能让子模块集成到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。
父模块中如此声明
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0.0</version>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.2</version>
</dependency>
...
<dependencies>
</dependencyManagement>
子模块中如此声明
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>project-a</artifactId>
...
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
作用
1、代码量减少,不用多次重复声明 version等元素。
2、version受父管理,父变则子变,便于修改。
3、dependencyManagement一旦定义,可以复用。
依赖管理导入
导入现有的依赖管理
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId> account-parent </artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
1.5.2.3 插件管理
和依赖管理类似,Maven提供了pluginManagement元素帮助管理插件。
1.5.3 聚合与继承的关系
区别 :
1.对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。
2.对于继承关系的父 POM来说,它不知道有哪些子模块继承与它,但那些子模块都必须知道自己的父 POM是什么。
共同点 :
1.聚合 POM与继承关系中的父POM的 packaging都是pom
2.聚合模块与继承关系中的父模块除了 POM之外都没有实际的内容。
注:在现有的实际项目中一个 POM既是聚合POM,又是父 POM,这么做主要是为了方便
1.5.4 反应堆
在一个多模块的Maven项目中,反应堆(Reactor)是指所有模块组成的一构建结构。对于单模块的项目,反应堆就是该模块本身,但是对于多模块的项目来说,反应堆包含了各个模块之间的继承和依赖的关系,从而能够自动计算出合理的模块构建顺序。
裁剪反应堆
mvn clean install参数模块1,模块2…,具体参数如下:
-am:同时构建所列模块的依赖模块
-amd:同时构建依赖于所列模块的模块
-pl:构建指定模块
-rf:从指定的模块返回一个反应堆对象
技巧:如果想编译模块之间前后顺序mvn clean compile,等出现“Reactor Build Order”后直接Ctrl +C中断,或者直接mvn clean
2 常用命令
定义骨架
mvn archetype:generate
查看当前项目的已解析依赖
mvn dependency:list
查看当前项目的依赖树
mvndependency:tree
分析当前项目的依赖
mvndependency:analyze(只会分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行是需要的依赖他就发现不了,因此对于分析出来的Unused declared dependencies不应立马删除,需要仔细分析后再做决定)。
安装并且强制检查更新
mvn clean install-U
编译并且打印详细信息
mvn compile -X
查看插件目标的默认绑定阶段
:mvn help:describe –Dplugin=org.apache.maven.plugins:maven-source-plugin:2.1.1 –Ddetail
查看插件信息
:mvn help:describe –Dplugin=org.apache.maven.plugins:maven-compiler-plugin:2.1 –Ddetail
mvn help:describe –Dplugin=org.apache.maven.plugins:maven-compiler-plugin
mvn help:describe –Dplugin=compiler [–Ddetail]【可以加上–Ddetail参数】
mvn help:describe –Dplugin=compiler-Dgoal=compile【仅仅查看插件目标信息】
跳过测试
mvn install –Dmaven.test.skip=true【改变插件目标参数】
3、常用配置
定义jar包的最终名称:finalName可以定义jar包的文件名称
<project>
<groupId>aaa</groupId>
<artifactId>bbb</artifactId>
<version>2.0</version>
<finalName>aaa.bbb-20.jar</finalName
</project>
打包的时候配置manifest信息
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.juvenxu.mavenbook.HelloWorldCli</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
依赖配置
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
<type>jar</type> //依赖的类型,对应项目坐标定义的packaging,默认jar
<scope>test</scope>//依赖范围
<optional>true</optional> //标记依赖是否可选
<exclusions>//用来排除传递性依赖
<exclusion>
<groupId>com.aaa</groupId>//排除依赖不需要指定版本
<artifactId>bbb</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
系统依赖配置
systemPath元素可以引用环境变量,如
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jabc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
定义属性
<project>
<groupId>aaa</groupId>
<artifactId>bbb</artifactId>
<version>2.0</version>
<properties>
<springframe.version>2.5.6</springframe.version>
</properties
</project>
本地仓库目录设置
<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 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:\java\repository</localRepository>
</settings>
设置远程仓库(下载依赖)
<project>
...
<repositories>
<repository>
<id>jboss</id>
<name>JBoss Repository</name>
<url>http://repository.jboss.com/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<snapshots>
<enabled>false</enabled>
<updatePolicy>daily</updatePolicy>//never-从不检查更新,always-每次构建都检查更新,daily-每天检查一次,默认的选,X-每X分钟检查一次更新
<checksumPolicy>ignore</checksumPolicy>//检验和验证对应的校验和文件失败后的策略,默认warn警告,fail-遇到校验和错误就让构建失败,ignore-完全忽略校验和错误。
</snapshots>
<layout>default</layout>
</repositories>
...
</project>
注意:这里的ID要和setting.xml中server设置远程仓库认证ID一致。
部署(依赖)至远程仓库
<project>
...
<destributionManagement>
<repository>
<id>proj-releases</id>
<name>Proj Release Repository</name>
<url>http://192.168.1.100/content/repositories/proj-releases</url>
</repository>
<snapshotRepository>
<id>proj-snapshots</id>
<name>Proj Snapshot Repository</name>
<url>http://192.168.1.100/content/repositories/proj-snapshots</url>
</snapshotRepository>
</destributionManagement>
...
</project>
注意:这里的ID要和setting.xml中server设置远程仓库认证ID一致。
远程仓库认证
<settings>
...
<servers>
<server>
<id>my-proj</id>
<username>repo-user</username>
<password>repo-pwd</password>
</server>
</servers>
...
</settings>
注意:这里的ID要和POM中设置远程仓库的ID一致。
配置中央仓库镜像
<settings>
...
<mirrors>
<mirror>
<id>maven.net.cn</id>
<name>one of the central mirrors in china</name>
<url>http://maven.net.cn/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
...
</settings>
配置所有仓库的镜像—私服,私服就是所有仓库的镜像
<settings>
...
<mirrors>
<mirror>
<id>internal-repository</id>
<name>Internal Repository Manager</name>
<url>http://192.168.1.100/maven2</url>
<mirrorOf>*</mirrorOf>//匹配所有的远程仓库
</mirror>
</mirrors>
...
</settings>
注意:如果需要认证,那么需要配置一个id为internal-repository的<server>认证信息。
自定义绑定插件目标,比如verify阶段生成doc文件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
配置 编译的 JDK 版本
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
或者
<profiles>
<profile>
<id>jdk-1.6</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.6</jdk>
</activation>
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
<maven.compiler.compilerVersion>1.6</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
POM中配置插件任务,比如调用ant任务
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>ant-validate</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>I'm bound to validate phase.</echo>
</tasks>
</configuration>
</execution>
<execution>
<id>ant-verify</id>
<phase>verify</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>I'm bound to verify phase.</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
设置远程仓库(下载插件)
<project>
...
< pluginRepositories>
<pluginRepository>
<id>nexus</id>
<url>http:// </url>
<releases>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</pluginRepository>
</ pluginRepositories>
...
</project>
注意:这里的ID要和setting.xml中server设置远程仓库认证ID一致。
4、常见问题
1、POM和setting.xml中配置依赖和插件有什么区别(或者说配置远程仓库)?
POM是针对一个POM文件,setting.xml中针对全局,后者可以减少重复配置,后者需要通过配置profile来实现。