Maven知识集锦
- 项目对象模型(Project Object Model,POM):描述项目的基本信息:声明构件的坐标、如何进行构建、声明项目的依赖存放在项目根目录下的pom.xml中。POM包含了项目的一切元数据——具体IDE相关的元数据,例如.project、.classpth、.settings,均不再需要,IDE的Maven插件可以根据POM自动生成
- 构件(Artifact):每个项目都抽象为一个构件。多模块项目(父子项目)包含多个构件构件主要有三类:普通包、插件、原型
- 坐标:每个构件使用坐标来唯一识别,坐标主要包括3个维:组标识(groupId)、构件标识(artifactId)、版本号(version),另外两个次要维度为:打包方式(packaging)和分类器(classifer)。构件的字符串表示通常为:groupId:artifactId:version。构件的文件命名格式:artifactId-version-classifer.packaging,例如spring-jdbc-3.0.6.RELEASE.jar
开发中的版本,version往往有SNAPSHOT后缀,例如0.0.1-SNAPSHOT
打包方式为可选字段,包括pom、jar、war、ear、maven-plugin、maven-archetype等,默认为jar
分类器为可选字段,通常可用于区分构件对应的jdk版本 - 依赖(Dependency):描述当前构件的生命周期的某个范围(Scope)内依赖的其他构件,有效的范围取值包括:
- compile:默认值,适用于所有阶段,随项目发布
- provided:期望容器或者jdk提供的类,例如对servlet-api的依赖
- runtime:仅仅在运行时使用,例如具体的jdbc驱动,可用于单元测试
- test:仅仅在单元测试时使用
- system:不在Repository中查找,需要指定本地路径,一般不用
- 传递性依赖(Transitive dependencies):由于每个构件都可以依赖于其他构件,因此可以形成依赖的传递链
- 可选依赖(optional):标记为可选的依赖,不构成依赖传递链条
- 插件(Plugin):Maven本身是一个框架,一切具体的工作都由插件完成,每个插件包含多个目标。插件本身也是一种构件,存放于仓库(Repository)中,即用即下载
- 目标(Goal):目标是明确定义的工作单元,可以被Maven单独调用完成特定的操作,或者作为生命周期某阶段的一个步骤,与其他目标一起被调用。
目标的字符串表示为:pluginId:goalId - Mojo:即“Maven plain Old Java Object”,就是插件的目标的另外一个称呼
- Maven生命周期(Lifecycle):生命周期是包含在一个项目构建中的一系列有序的阶段。Maven支持多种生命周期,包括清理生命周期、默认生命周期、站点生命周期。主要被使用的是默认生命周期
- 阶段(Phase):生命周期包含一系列的阶段(Phase),下图展示运行mvn clean install时,包含的阶段:
对于不同项目类型,同一阶段的行为可以不同,例如,一般项目的package阶段是打jar包,web项目则是打war包。
插件的目标可以附在特定Maven生命周期阶段上,并随着生命周期的推进而被调用。例如对于jar项目:jar:jar目标就默认附在package阶段, surefire:test则附在test阶段。位于后面的阶段被运行时,前置阶段自动被运行,例如对于 mvn package 命令,不仅仅运行打包阶段,编译、测试阶段都会执行(clean不会执行,因为clean不属于默认生命周期的组成部分)。如果某个阶段运行失败,后续默认阶段不会运行 - 仓库(Repository):首次添加依赖包,或者首次运行某个Maven命令(即调用插件),会看到控制台上显示正在下载各种组件,下载都是从仓库获得构件的。安装Maven时,仅安装2M左右的核心框架,所有构件(依赖包、插件),均在需要时到“仓库”下载。构件相对于仓库的根目录的文件路径的形式为:
/groupId/artifactId/version/artifactId-version.packaging
其中,如果groupId有点号,则分解为多级目录,例如:
/cn/com/sparknet/hibernate-tools/0.0.1/hibernate-tools-0.0.1.jar
构件所在目录,还包括构件的pom文件、构件源码文件、javadoc文件等内容 - 本地仓库:本地仓库存放已经下载的构件,避免重复下载;也存放本地构建的构件默认位置为 ~/.m2/repository
- 索引:仓库,特别是中央仓库非常大,索引用于加快构件查询的速度。仓库的管理者(软件,例如nexus)负责生成和维护索引,使用者下载索引
- 原型(Archtype):原型是预定义的模板,可以用来生成项目的骨架。一般的,新建Maven项目均要指定其原型。原型也是构件的一种,可以自己开发原型,满足不同项目的需要
- 构件版本的命名规则:主版本、次版本、增量版本之间使用点号.间隔,如果有里程碑版本号,则与前者使用-间隔。例如:
1.1.1-beta-1
主版本,一般表示重大架构的变更,比如Spring2与Spring3
次版本,表示大范围功能的增加和变化,比如Spring2.5比2.0增加注解功能
增量版本,一般表示重大缺陷的修复
里程碑版本,一般表示一个里程碑,比如alpha、beta等
通常增量版本、里程碑版本是可选的 - 快照(SNAPSHOT)版本:是一种特殊的里程碑版本,表示开发的最新状态,快照版本不指向一个特定的构件,而是指向其最新发布的引用。在仓库中,快照版本具有特殊的命名规则,例如: mom-tools-0.0.1-20111028.003655-3.jar 表示mom-tools构件的0.0.1的快照版本,此版本在2011年10月28日发布,是0.01快照的第3次发布。如果快照仓库的updatePolicy设置为always,那么每次构建都会自动从快照仓库下载最新的发布。使用 mvn -U 参数,也可以强制从仓库下载最新发布
下面是一个简单的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<settings> <localRepository>D:\JavaEE\maven\maven_repository</localRepository> <pluginGroups></pluginGroups> <offline>false</offline> <servers> <server> <id>spkntNexus</id> <username>wangzhen</username> <password>lavender</password> </server> </servers> <proxies /><mirrors /> <profiles> <profile> <id>spkntDev</id> <activation> <activeByDefault>false</activeByDefault> <jdk>1.5</jdk> <os><name>Windows XP</name></os> </activation> <repositories> <repository> <id>nexus</id> <url>http://192.168.1.15:28080/nexus/content/groups/public/</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </repository> </repositories> <pluginRepositories></pluginRepositories> </profile> </profiles> <activeProfiles> <activeProfile>spkntDev</activeProfile> </activeProfiles> </settings> |
各元素说明如下:
- settings是固定的根元素
- localRepository指定本地仓库的文件系统路径
- pluginGroups 使用某插件时,如果不指定groupId,使用此元素下面定义的值
- proxies 联网时的代理设置
- servers 服务器设置,主要是用户名密码,与pom的distributionManagement配合使用,可完成构件的远程发布
- mirrors 为某些仓库配置镜像
- profiles 包含可选参数的配置,通过激活/禁用特定的profile,灵活的切换配置
- profile/activation 设置profile自动激活规则,比如运行于WindowsXp则激活
- profile/repositories 定义远程仓库
- profile/pluginRepositories 定义远程插件仓库
- activeProfiles 设置启用的profile
下面是一个简单的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<project> <modelVersion>4.0.0</modelVersion> <groupId>sparknet.training</groupId> <artifactId>mockito</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <name>mockito</name> <parent>mockito-parent</parent> <modules/> <properties> <spring-version>3.0.6</spring-version> </properties> <dependencies></dependencies> <build> <directory/> <sourceDirectory/> <resources/> <plugins></plugins> <pluginManagement></pluginManagement> </build> <profiles></profiles> <distributionManagement/> </project> |
各元素说明如下:
- project元素是pom固定的根元素
- groupId、artifactId、version、packaging定义了构件坐标
- parent、modules元素用于多maven模块的项目:parent在模块中使用,指向其父项目;modules在父项目中使用,声明其包含的模块列表
- properties用于自定义属性,或者覆盖系统属性。自定义的属性可以在pom中以 ${propertyName} 的形式引用
- dependencies用于管理当前构件的依赖
- build 与构建过程相关:
- directory用于指定输出目录,类似有outputDirectory、testOutputDirectory
- sourceDirectory用于指定源码目录,默认src/main/java
- testSourceDirectory用于指定测试源码目录,默认src/test/java
- resources用于指定资源目录,默认src/main/resources。资源目录可以指定排除/包含规则,比如排除:**/*.java
- testResources用于指定测试资源目录,默认src/test/resources
- plugins用于指定/覆盖插件配置、定义新的插件调用,比如指定源码级别、指定源码编码方式。注意,如果profiles覆盖外面(即build/plugins/plugin/...)的某个插件的配置属性,必须明确声明新的属性值,否则,会从外面带入属性值
- pluginManagement 用于在父子项目中,指定默认的插件配置
- profiles用于定义可选的配置信息,可以定义上述大部分内容,通过 mvn -P 参数可以激活profile
- distributionManagement 定义构件的发布管理
目标都可以附着到其支持的Maven生命周期阶段,在插件配置中声明类似下面的片段:
1 2 3 4 5 6 |
<execution> <phase>package</phase> <goals> <goal>single</goal> </goals> ... |
即可指定目标在哪个阶段执行,这样,早期阶段中的目标自然先执行。
如果两个目标在声明在同一阶段执行,那么,它们在POM中声明的顺序,决定了它们执行的顺序,先声明的先执行。
自己编写的插件,调用时需要提供完成的插件坐标:
1 2 |
# 插件坐标 # 目标名称 mvn cc.gmem.yun.maven.plugins:maven-archetype-plugin:create-from-project |
同时,也需要在build/plugins中声明自己编写的插件版本。
下面的例子示意如何将create-from-project目标绑定到install阶段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<build> <plugins> <plugin> <groupId>cc.gmem.yun.maven.plugins</groupId> <artifactId>maven-archetype-plugin</artifactId> <version>3.1.1</version> <configuration> <propertyFile>archetype.properties</propertyFile> <archetypePostPhase>install</archetypePostPhase> </configuration> <executions> <execution> <phase>install</phase> <goals> <goal>create-from-project</goal> </goals> </execution> </executions> </plugin> </plugins> </build> |
例如要分析构件spring-data-mongodb的冲突,执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#显示从解析后依赖树中忽略的冲突 # 包含在结果里的构件 # [groupId]:[artifactId]:[type]:[version] # 支持通配符,:::*-SNAPSHOT 表示所有快照构件 mvn dependency:tree -Dverbose -P dang -Dincludes=*:spring-data-mongodb # 输出示例: # [INFO] com.dangdang.digital.read:media-cms:war:1.0.0 # [INFO] +- com.dangdang.base:account-api:jar:1.1.0:compile # [INFO] | \- com.dangdang.base:framework:jar:1.0.5:compile # [INFO] | \- org.springframework:spring-data-mongodb:jar:1.0.0.M5:compile # [INFO] \- org.springframework.data:spring-data-mongodb:jar:1.1.1.RELEASE:compile # 可以看到通过account-api获得的传递依赖,和直接指定的1.1.1版本依赖冲突 |
传递性依赖导致的版本冲突,如下面的例子,出现cglib包的冲突,如何调解?
自动调解规则:
- 最近路径优先原则,即从当前构件到冲突构件的路径长度短者优先
- 最先声明原则,即在POM中声明顺序靠前者优先(2.0.9+)
手工调解:在POM中声明传递性依赖的排除。通常开源的工具包向后兼容(Backwards compatibility),我们倾向于排除低版本的依赖
可以使用镜像解决,把公司私有仓库设置为其它所有仓库的镜像:
1 2 3 4 5 6 7 8 |
<mirrors> <mirror> <!-- 创建一个名为maven的镜像,它是central仓库的镜像,对central的访问自动 转给它--> <id>maven</id> <mirrorOf>central</mirrorOf><!-- 可以使用通配符 * --> <url>http://repo1.maven.org/maven2</url> </mirror> </mirrors> |
设置环境变量:set MAVEN_OPTS=-Xms128m -Xmx512m
或者修改mvn.bat。
在Eclipse中运行时,可以修改Run Configurations,增加JVM参数
在Run Configuration中勾选“Resolve Workspace artifacts”
报错:Unable to update index for...解决步骤:
先创建容器项目:创建简单项目,packaging设置为pom
容器项目点击右键:Maven - New Maven Module Project
修改Maven设置,添加速度较快的镜像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<mirrors> <mirror> <id>google-maven-central</id> <name>Google Maven Central</name> <url>https://maven-central.storage.googleapis.com</url> <mirrorOf>central</mirrorOf> </mirror> <mirror> <id>oschina</id> <mirrorOf>central</mirrorOf> <name>OSChina</name> <url>http://maven.oschina.net/content/groups/public/</url> </mirror> </mirrors> |
如果你的机器无法连接到Maven仓库,可以使用网络代理:
1 2 3 4 5 6 7 8 9 10 |
<proxies> <proxy> <id>zircon</id> <active>true</active> <protocol>http</protocol> <host>127.0.0.1</host> <port>8087</port> <nonProxyHosts>127.0.0.1|192.168.0.*</nonProxyHosts> </proxy> </proxies> |
代理类型一般使用http,我尝试了socks5,发现无法连通,不知道什么原因。
[ERROR] Missing message: configure.incompatibleComplianceForSource in: org.aspectj.ajdt.ajc.messages
<unknown source file>:<no line information>
升级AspectJ到1.8.8,aspectj-maven-plugin到1.8,然后修改后者的插件配置:
1 2 3 4 5 6 |
<configuration> <source>${java.version}</source> <target>${java.version}</target> <!-- 添加下面这个配置项 --> <complianceLevel>${java.version}</complianceLevel> </configuration> |
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.1.1:war (default-war) on project ***: Failed to copy file for artifact [***:jar:0.0.1-SNAPSHOT:compile]: /home/alex/JavaEE/projects/idea/pems-trunk/comm-tools/target/classes (Is a directory) -> [Help 1]
maven-war-plugin版本过低,需要在POM中明确的声明使用新版:
1 2 3 4 5 |
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.6</version> </plugin> |
无法找到依赖,会显示依赖的名字,其中的占位符${}应该已经被替换为实际值。
需要配置扩展,否则此属性不可用。
1 2 3 4 5 6 7 8 9 |
<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.1</version> </extension> </extensions> </build> |
子命令 | 说明 |
clean | 清理项目下已经生成的类、资源文件。此步骤不会自动执行 |
test | 执行已经定义的单元测试 |
compile | 编译源码到target目录 |
package | 打包为jar war ear到 target目录 |
install | 安装到本地仓库 |
deploy | 部署到远程仓库,需要结合maven配置 |
dependency:copy-dependencies -Dmdep.useRepositoryLayout=true |
导出依赖包,保持repository中的目录结构 |
dependency:copy-dependencies -DincludeScope=compile |
导出依赖包,到target目录,只包含compile范围的依赖 |
archetype:create | 从原型创建项目,例如创建web项目: |
1 2 3 |
mvn archetype:create -DgroupId=? -DartifactId=? -Dpackaging=jar -DarchetypeArtifactId=maven-archetype-webapp |
eclipse:eclipse生成Eclipse项目结构,包括.classpath等元数据,例如:
1 |
mvn eclipse:eclipse –Dwtpversion=1.0 |
help:describe描述一个插件,打印其用法,例如:
mvn help:describe -Dplugin=help help:effective-settings查看当前实际有效的settingshelp:effective-pom查看当前项目实际起作用的pom,包含默认配置,因此比pom.xml全面dependency:resolve
dependency:tree打印依赖assembly:assembly生成可执行jar包
选项 | 说明 |
-U | 强制更新快照依赖 |
-o | 离线模式,不去联网获取依赖 |
-X -e | 启用DEBUG模式 |
-Dmaven.test.skip=true -Dmaven.test.skip |
跳过测试 |
-Dcheckstyle.skip | 跳过代码风格检查 |
-Dmaven.test.failure.ignore=true | 即使测试失败,生命周期后续阶段仍然执行 |
-P pname | 启用指定的Profile |
执行下面的命令,可以交互式的生成新的多模块工程:
1 2 3 4 |
mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=pom-root -DarchetypeVersion=RELEASE # 根据提示输入组、构件标识符以及版本号 # 注意packaging要设置为pom,表示创建一个多模块容器工程 |
1 2 3 4 |
# 进入容器工程所在目录,创建模块 cd parent-artifact-name mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=RELEASE |
在任意目录执行如下命令,可以获得当前使用的全局、用户设置文件的路径:
1 2 3 |
mvn -X | grep 'Reading.*settings\s' # [DEBUG] Reading global settings from /Users/Alex/JavaEE/maven/3.3.9/conf/settings.xml # [DEBUG] Reading user settings from /Users/Alex/.m2/settings.xml |
Leave a Reply