Maven,发音是[`meivin],"专家"的意思。它是一个很好的项目管理工具,很早就进入了我的必备工具行列,但是这次为了把project1项目完全迁移并应用maven,所以对maven进行了一些深入的学习。写这个学习笔记的目的,一个是为了自己备忘,二则希望能够为其他人学习使用maven 缩短一些时间。
首先我把maven的概念快速的梳理一下,让我们快速地建立起一个比较精确的maven应用场景。
读书时候要先限定范围,避免一些有害的遐想。要说maven不是什么,我们可以从如下几个要点来展开
maven将自己定位为一个项目管理工具。它负责管理项目开发过程中的几乎所有的东西:
maven把项目的构建划分为不同的生命周期(lifecycle),在我看来,划分的已经是非常仔细了,大家可以参考这里 。粗略一点的话,它这个过程(phase)包括:编译、测试、打包、集成测试、验证、部署。maven中所有的执行动作(goal)都需要指明自己在这个过程中的执行位置,然后maven执行的时候,就依照过程的发展依次调用这些goal进行各种处理。
这个也是maven的一个基本调度机制。一般来说,位置稍后的过程都会依赖于之前的过程。当然,maven同样提供了配置文件,可以依照用户要求,跳过某些阶段。
所谓的"约定优于配置",在maven中并不是完全不可以修改的,他们只是一些配置的默认值而已。但是使用者除非必要,并不需要去修改那些约定内容。maven默认的文件存放结构如下:
每一个阶段的任务都知道怎么正确完成自己的工作,比如compile任务就知道从src/main/java下编译所有的java文件,并把它的输出class文件存放到target/classes中。
对maven来说,采用"约定优于配置"的策略可以减少修改配置的工作量,也可以降低学习成本,更重要的是,给项目引入了统一的规范。
maven使用如下几个要素来唯一定位某一个输出物: groupId:artifactId:packaging:version 。比如 org.springframework:spring:2.5 。每个部分的解释如下:
maven有自己的版本规范,一般是如下定义
maven在版本管理时候可以使用几个特殊的字符串 SNAPSHOT ,LATEST ,RELEASE 。比如"1.0-SNAPSHOT"。各个部分的含义和处理逻辑如下说明:
maven把整个maven管理的项目分为几个部分,一个部分是源代码,包括源代码本身、相关的各种资源,一个部分则是单元测试用例,另外一部分则是各种maven的插件。对于这几个部分,maven可以独立管理他们,包括各种外部依赖关系。
依赖管理一般是最吸引人使用maven的功能特性了,这个特性让开发者只需要关注代码的直接依赖,比如我们用了spring,就加入spring依赖说明就可以了,至于spring自己还依赖哪些外部的东西,maven帮我们搞定。
任意一个外部依赖说明包含如下几个要素:groupId, artifactId, version, scope, type, optional。其中前3个是必须的,各自含义如下:
maven认为,程序对外部的依赖会随着程序的所处阶段和应用场景而变化,所以maven中的依赖关系有作用域(scope)的限制。在maven中,scope包含如下的取值:
另外,代码有代码自己的依赖,各个maven使用的插件也可以有自己的依赖关系。依赖也可以是可选的,比如我们代码中没有任何cache依赖,但是hibernate可能要配置cache,所以该cache的依赖就是可选的。
maven的多项目管理也是非常强大的。一般来说,maven要求同一个工程的所有子项目都放置到同一个目录下,每一个子目录代表一个项目,比如
按照这种格式存放,就是继承方式,所有具体子项目的pom.xml都会继承总项目pom的内容,取值为子项目pom内容优先。
要设置继承方式,首先要在总项目的pom中加入如下配置
其次在每个子项目中加入
即可。
当然,继承不是唯一的配置文件共用方式,maven还支持引用方式。引用pom的方式更简单,在依赖中加入一个type为pom的依赖即可。
用户可以在maven中定义一些属性,然后在其他地方用${xxx}进行引用。比如:
profile是maven的一个重要特性,它可以让maven能够自动适应外部的环境变化,比如同一个项目,在linux下编译linux的版本,在win下编译win的版本等。一个项目可以设置多个profile,也可以在同一时间设置多个profile被激活(active)的。自动激活的 profile的条件可以是各种各样的设定条件,组合放置在activation节点中,也可以通过命令行直接指定。profile包含的其他配置内容可以覆盖掉pom定义的相应值。如果认为profile设置比较复杂,可以将所有的profiles内容移动到专门的 profiles.xml 文件中,不过记得和pom.xml放在一起。
activation节点中的激活条件中常见的有如下几个:
maven的操作有两种方式,一种是通过mvn命令行命令,一种是使用maven的eclipse插件。因为使用eclipse的maven插件操作起来比较容易,这里就只介绍使用mvn命令行的操作。
maven的主执行程序为mvn.bat,linux下为mvn.sh,这两个程序都很简单,它们的共同用途就是收集一些参数,然后用 java.exe来运行maven的Main函数。maven同样需要有配置文件,名字叫做settings.xml,它放在两个地方,一个是maven 安装目录的conf目录下,对所有使用该maven的用户都起作用,我们称为主配置文件,另外一个放在 %USERPROFILE%/.m2/settings.xml下,我们成为用户配置文件,只对当前用户有效,且可以覆盖主配置文件的参数内容。还有就是项目级别的配置信息了,它存放在每一个maven管理的项目目录下,叫pom.xml,主要用于配置项目相关的一些内容,当然,如果有必要,用户也可以在 pom中写一些配置,覆盖住配置文件和用户配置文件的设置参数内容。
一般来说,settings文件配置的是比如repository库路径之类的全局信息,具体可以参考官方网站的文章 。
要创建一个新的maven工程,我们需要给我们的工程指定几个必要的要素,就是maven产品坐标的几个要素:groupId, artifactId,如果愿意,你也可以指定version和package名称。我们先看一个简单的创建命令:
d:\work\temp>mvn archetype:create -DgroupId=com.abc -DartifactId=product1 -DarchetypeArtifactId=maven-archetype-webapp
首先看这里的命令行参数的传递结构,怪异的 -D参数=值 的方式是 java.exe 要求的方式。这个命令创建一个web工程,目录结构是一个标准的maven结构,如下:
大家要注意,这里目录结构的布局实际上是由参数 archetypeArtifactId 来决定的,因为这里传入的是 maven-archetype-webapp 如果我们传入其他的就会创建不同的结构,默认值为 maven-archetype-quickstart ,有兴趣的读者可以参考更详细的列表 ,我把部分常用的列表在这里:
Artifact | Group | Version | Repository | Description |
---|---|---|---|---|
maven-archetype-j2ee-simple | org.apache.maven.archetypes | A simple J2EE Java application | ||
maven-archetype-marmalade-mojo | org.apache.maven.archetypes | A Maven plugin development project using marmalade | ||
maven-archetype-plugin | org.apache.maven.archetypes | A Maven Java plugin development project | ||
maven-archetype-portlet | org.apache.maven.archetypes | A simple portlet application | ||
maven-archetype-profiles | org.apache.maven.archetypes | |||
maven-archetype-quickstart | org.apache.maven.archetypes | |||
maven-archetype-simple | org.apache.maven.archetypes | |||
maven-archetype-site-simple | org.apache.maven.archetypes | A simple site generation project | ||
maven-archetype-site | org.apache.maven.archetypes | A more complex site project | ||
maven-archetype-webapp | org.apache.maven.archetypes | A simple Java web application | ||
maven-archetype-har | net.sf.maven-har | 0.9 | Hibernate Archive | |
maven-archetype-sar | net.sf.maven-sar | 0.9 | JBoss Service Archive |
大家可以参考更详细的 archetype:create 帮助 和 archtype参考信息 。
多项目管理是maven的主要特色之一,对于一个大型工程,用maven来管理他们之间复杂的依赖关系,是再好不过了。maven的项目配置之间的关系有两种:继承关系和引用关系。
maven默认根据目录结构来设定pom的继承关系,即下级目录的pom默认继承上级目录的pom。要设定两者之间的关系很简单,上级pom如下设置:
要记住的是,这里的module是目录名,不是子工程的artifactId。子工程如下设置:
这样两者就相互关联起来了,继承关系就设定完毕,所有父工程的配置内容都会自动在子工程中生效,除非子工程有相同的配置覆盖。如果你不喜欢层层递进的目录结构来实现继承,也可以在parent中加入
引用关系是另外一种复用的方式,maven中配置引用关系也很简单,加入一个 type 为 pom 的依赖即可。
但是无论是父项目还是引用项目,这些工程都必须用 mvn install 或者 mvn deploy 安装到本地库才行,否则会报告依赖没有找到,eclipse编译时候也会出错。
需要特别提出的是复用过程中,父项目的pom中可以定义 dependencyManagement 节点,其中存放依赖关系,但是这个依赖关系只是定义,不会真的产生效果,如果子项目想要使用这个依赖关系,可以在本身的 dependency 中添加一个简化的引用
这种方法可以避免版本号满天飞的情况。
在maven中一般都会用到安装库文件的功能,一则是我们常用的hibernate要使用jmx库,但是因为sun的license限制,所以无法将其直接包含在repository中。所以我们使用mvn命令把jar安装到我们本地的repository中
mvn install:install-file -DgroupId=com.sun.jdmk -DartifactId=jmxtools -Dversion=1.2.1 -Dpackaging=jar -Dfile=/path/to/file
如果我们想把它安装到公司的repository中,需要使用命令
mvn deploy:deploy-file -DgroupId=com.sun.jdmk -DartifactId=jmxtools -Dversion=1.2.1 -Dpackaging=jar -Dfile=/path/to/file -Durl=http://xxx.ss.com/sss.xxx -DrepositoryId=release-repo
对于我们的工程输出,如果需要放置到公司的repository中的话,可以通过配置pom来实现
这里使用的scp方式提交库文件,还有其他方式可以使用,请参考faq部分。然后记得在你的settings.xml中加入这一内容
maven定义了很多变量属性,参考这里 http://docs.codehaus.org/display/MAVENUSER/MavenPropertiesGuide
我们已经知道maven预定义了许多的阶段(phase),每个插件都依附于这些阶段,并且在进入某个阶段的时候,调用运行这些相关插件的功能。我们先来看完整的maven生命周期:
生命周期 | 阶段描述 |
---|---|
validate | 验证项目是否正确,以及所有为了完整构建必要的信息是否可用 |
generate-sources | 生成所有需要包含在编译过程中的源代码 |
process-sources | 处理源代码,比如过滤一些值 |
generate-resources | 生成所有需要包含在打包过程中的资源文件 |
process-resources | 复制并处理资源文件至目标目录,准备打包 |
compile | 编译项目的源代码 |
process-classes | 后处理编译生成的文件,例如对Java类进行字节码增强(bytecode enhancement) |
generate-test-sources | 生成所有包含在测试编译过程中的测试源码 |
process-test-sources | 处理测试源码,比如过滤一些值 |
generate-test-resources | 生成测试需要的资源文件 |
process-test-resources | 复制并处理测试资源文件至测试目标目录 |
test-compile | 编译测试源码至测试目标目录 |
test | 使用合适的单元测试框架运行测试。这些测试应该不需要代码被打包或发布 |
prepare-package | 在真正的打包之前,执行一些准备打包必要的操作。这通常会产生一个包的展开的处理过的版本(将会在Maven 2.1+中实现) |
package | 将编译好的代码打包成可分发的格式,如JAR,WAR,或者EAR |
pre-integration-test | 执行一些在集成测试运行之前需要的动作。如建立集成测试需要的环境 |
integration-test | 如果有必要的话,处理包并发布至集成测试可以运行的环境 |
post-integration-test | 执行一些在集成测试运行之后需要的动作。如清理集成测试环境。 |
verify | 执行所有检查,验证包是有效的,符合质量规范 |
install | 安装包至本地仓库,以备本地的其它项目作为依赖使用 |
deploy | 复制最终的包至远程仓库,共享给其它开发人员和项目(通常和一次正式的发布相关) |
maven核心的插件列表可以参考 http://maven.apache.org/plugins/index.html 。这里仅列举几个常用的插件及其配置参数:
除了以下的几个faq条目之外,还有一些faq可以参考
兄弟们如果有其他问题,欢迎跟帖提问!
答:直接在pom文件中加入一个dependency节点,如果要删除依赖,把对应的dependency节点删除即可。
答:设置exclusion即可。
答:加入一个特殊的依赖关系,使用system类型,如下:
但是要记住,发布的时候不会复制这个jar。需要手工配置,而且其他project依赖这个project的时候,会报告警告。如果没有特殊要求,建议直接注册发布到repository。
答:在project属性中去掉java build path中对其他 project 的依赖关系,直接在pom中设置依赖关系即可
答:使用 assembly 插件即可。
答:maven本身在发布的时候,可以发布单纯的jar,也可以同时发布xxx-tests.jar和xxx-javadoc.jar(大家经常在repository中可以看到类似的东西)。我们自己的项目A要同时输出test.jar可以做如下的设置
修改pom.xml文件,将旧版jar的依赖内容中的版本直接修改为新版本即可。
将依赖的文件安装到本地库,用如下命令可以完成:
mvn install:install-file -Dfile=-DgroupId= -DartifactId= -Dversion= -Dpackaging= -DgeneratePom=true Where: the path to the file to load the group that the file should be registered under the artifact name for the file the version of the file the packaging of the file e.g. jar
答:使用资源过滤功能,比如:
答: maven-svn-revision-number-plugin 可以从 SVN 中获取版本号,并将其变成环境变量,交由其他插件或者profile使用,详细帮助在这里 。一般和resource的filter机制同时使用
这段代码负责把resource文件中的内容替换成适当内容
有好几种方法都可以实现跳过单元测试步骤,一种是给mvn增加命令行参数 -Dmaven.test.skip=true 或者 -DskipTests=true ;另外一种是给surefire插件增加参数,如下:
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<version>2.8version>
<configuration>
<skipTests>trueskipTests>
configuration>
plugin>
plugins>
build>
[...]
project>
可以,运行时候增加命令行参数 -Dtest=MyTest 即可,其中MyTest是所需要运行的单元测试用例名称,但是不需要包含package部分。
答:以下内容设定编译器编译java1.5的代码
答:指定source目录和test-source目录即可。
设置一个变量即可
{code:xml}... {code}... maven-compiler-plugin UTF-8
{code:xml}{code} org.codehaus.mojo native-maven-plugin true
{code:xml}[...] {code}[...] maven-assembly-plugin jar-with-dependencies
mvn assembly:assembly
{code:xml}... {code}... true src/main/command run.bat run.sh /abc src/main/scripts
{code:xml}{code} org.codehaus.mojo build-helper-maven-plugin 1.1 add-source generate-sources add-source
{code:xml}{code} http://www.cnblogs.com/src/java com.sun.enterprise hk2-maven-plugin com/sun/logging/LogDomains.* com/sun/enterprise/util/OS.java com/sun/enterprise/util/io/FileUtils.java com/sun/enterprise/util/zip/** com/sun/enterprise/util/i18n/** com/sun/enterprise/deployment/backend/IASDeploymentException.java com/sun/enterprise/config/ConfigBeansFactory.java com/sun/enterprise/config/clientbeans/** http://www.cnblogs.com/src/java **/*.properties
{code:xml}{code} src/java com.sun.enterprise hk2-maven-plugin
hibernate3-maven-plugin,按照如下配置:
{code:xml}{code} org.codehaus.mojo hibernate3-maven-plugin 2.1 hbm2ddl annotationconfiguration hsqldb hsqldb ${hsqldb.version}
答:当然可以,你可以使用插件 Tycho,详细内容可以参考这里[http://mattiasholmqvist.se/2010/02/building-with-tycho-part-1-osgi-bundles/].
另外,老牌的pde-maven-plugin就不要用了,已经好几年没见更新了。 org.sonatype.tycho target-platform-configuration 0.7.0 p2
使用专门的antrun插件,并且在target标签内部加入ant的代码
<plugin>
<artifactId>maven-antrun-pluginartifactId>
<version>1.6version>
<executions>
<execution>
<phase> phase>
<configuration>
<target>
target>
configuration>
<goals>
<goal>rungoal>
goals>
execution>
executions>
plugin>
2)如何在ant脚本中引用maven的classpath?
maven给每一个依赖都生成了一个属性,格式为"groupId:artifactId[:classifier]:type",比如,如果一下例子就显示依赖的org.apache.common-util的jar文件路径
<echo message="Dependency JAR Path: ${org.apache:common-util:jar}"/>
另外,maven还预定义了四个classpath的引用,他们是
3)如何使用antrun插件运行外部的build文件?
很简单,直接在antrun里边使用ant指令即可,如下:
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-antrun-pluginartifactId>
<version>1.6version>
<executions>
<execution>
<id>compileid>
<phase>compilephase>
<configuration>
<target>
<property name="compile_classpath" refid="maven.compile.classpath"/>
<property name="runtime_classpath" refid="maven.runtime.classpath"/>
<property name="test_classpath" refid="maven.test.classpath"/>
<property name="plugin_classpath" refid="maven.plugin.classpath"/>
<ant antfile="${basedir}/build.xml">
<target name="test"/>
ant>
target>
configuration>
<goals>
<goal>rungoal>
goals>
execution>
executions>
plugin>
true
{code:xml}[...] {code}[...] org.apache.maven.plugins maven-surefire-plugin true
reporting节点中加入则在
mvn site中执行,如果在
build节点中加入,则在build的时候自动运行检查。详细配置参考[pmd插件使用说明|http://maven.apache.org/plugins/maven-pmd-plugin/]
{code:xml}加入 checkstyle 检查,详细配置参考[checkstyle插件使用说明|http://maven.apache.org/plugins/maven-checkstyle-plugin/],同样注意放置在reporting和build节点中的区别(所有报表类插件都要同样注意):{code} org.apache.maven.plugins maven-pmd-plugin 2.5
{code:xml}加入 simian 的支持,simian是一个支持代码相似度检查的工具,目前有maven插件,也有checkstyle的插件。它不仅可以检查java,甚至可以支持文本文件的检查。详细帮助信息参考[这里|http://www.redhillconsulting.com.au/products/simian/]。simian 的 maven插件在[这里|http://mojo.codehaus.org/simian-report-maven-plugin/introduction.html]{code} org.apache.maven.plugins maven-checkstyle-plugin 2.5
{code:xml}加入 jdepend 检查,详细配置参考[jdepend使用说明|http://mojo.codehaus.org/jdepend-maven-plugin/],{code} ... org.codehaus.mojo simian-maven-plugin 1.6.1
{code:xml}加入 findbugz 检查,详细配置参考[findbugz使用说明|http://mojo.codehaus.org/findbugs-maven-plugin/usage.html],{code} org.codehaus.mojo jdepend-maven-plugin 2.0-beta-2
{code:xml}加入javadoc生成,详细配置参考[javadoc usage|http://maven.apache.org/plugins/maven-javadoc-plugin/usage.html]{code} org.codehaus.mojo findbugs-maven-plugin 2.0.1
{code:xml}加入 jxr 支持,JXR是一个生成java代码交叉引用和源代码的html格式的工具,详细配置信息参考[jxr usage|http://maven.apache.org/plugins/maven-jxr-plugin/]。注意,jxr没有必要在build阶段运行。{code} org.apache.maven.plugins maven-javadoc-plugin 2.7 ...
{code:xml}加入 Cobertura 支持,它是一个代码覆盖率工具,可以用来评估具有相应测试的源代码的比率。详细帮助在[这里|http://mojo.codehaus.org/cobertura-maven-plugin/index.html]。另外一个功能相似的软件是[EMMA|http://emma.sourceforge.net/samples.html],详细的帮助在[这里|http://mojo.codehaus.org/emma-maven-plugin/usage.html]。两个产品的比较文章在[这里|http://www.topcoder.com/tc?module=Static&d1=features&d2=030107],个人倾向于都要用,因为给出的指标不一样,都有参考作用。{code} org.apache.maven.plugins maven-jxr-plugin 2.1
{code:xml|title=Cobertura }{code} org.codehaus.mojo cobertura-maven-plugin 2.4 85 85 true 85 85 85 85 com.example.reallyimportant.* 90 80 com.example.boringcode.* 40 30 clean check
{code:xml|title=EMMA}添加 javaNCSS 插件,它是一个java代码的度量工具,详细参考在[这里|http://mojo.codehaus.org/javancss-maven-plugin/]。... {code}... ...... org.codehaus.mojo emma-maven-plugin 1.0-alpha-3-SNAPSHOT
{code:xml}h4. profile相关{code} org.codehaus.mojo javancss-maven-plugin 2.0-beta-2
{code:xml}h4. 部署相关{code} !environment.type
{code:xml}*# 使用ssh2配置proficio-repository Proficio Repository file://${basedir}/target/deploy {code}
{code:xml}*# 使用sftp配置proficio-repository Proficio Repository scp://sshserver.yourcompany.com/deploy {code}
{code:xml}*# 使用外在的ssh配置编译扩展用于指定使用wagon外在ssh提供,用于提供你的文件到相应的远程服务器。proficio-repositoryi Proficio Repository sftp://ftpserver.yourcompany.com/deploy {code}
{code:xml}*# 使用ftp配置proficio-repository Proficio Repository scpexe://sshserver.yourcompany.com/deploy org.apache.maven.wagon wagon-ssh-external 1.0-alpha-6 {code}
{code:xml}{panel} h4. 插件配置proficio-repository Proficio Repository ftp://ftpserver.yourcompany.com/deploy org.apache.maven.wagongroupId> wagon-ftpartifactId> 1.0-alpha-6version> {code}
{code:xml} ...... {code} org.apache.maven.plugins maven-site-plugin 2.0-beta-6 UTF-8
http://www.cnblogs.com/bigtall/archive/2011/03/23/1993253.html