1 Maven1.1 Maven是什么maven起源于apache组织为了简化其JakartaTurbine项目的编译,而开发的一套项目管理工具。接触maven之前,maven对你的印象可能是: 1) build工具? 2) 依赖管理工具? 3) 只是遵循了“Convention over Coding”原则,可以重用的ant脚本? 实际上,maven究竟是什么?答案是yes,but不仅仅是以上几个方面: Maven是一个软件项目管理工具和理解工具。 Maven通过POM(ProjectObject Model)对一个项目的应该具有的信息进行了标准化;在POM的基础上,定义一套统一的项目编译和发布项目成果和信息的流程,并能让该项目成果在多个项目之间共享。 Maven的目标是什么: 1) 让每个项目的编译过程清晰并且一致; 2) 提供了一套统一的构建流程; 3) 提供了项目开发过程中信息发布的机制; 4) 遵循了一套软件工程的最佳实践,鼓励组件解耦与重用(源码重用只是最初级的重用,重用的级别越高,带来的收益越大) 1.2 Maven的配置1) Global settings:$M2_HOME/conf/settings.xml 2) User Settings:LocalRepository位置下的的settings.xml 3) User的Setting可以覆盖Global中的设置(就近原则)。 1.3 Maven初体验Simple Maven Project Demo 2种与maven交互的方式:mvnLifeCycle/mvn pluginId:goal Maven Central Repository:http://central./maven2/ Maven仓库的布局: /<groupId>/<artifactId>/<version>/<artifactId>-<version>.<packaging> 在哪里搜索artifact的GAV? 1) http://search. central仓库的供查询版本; 3) http://www. 4) http:// 5) http://www. 6) http://www. 1.4 Maven项目的标准布局表1?1 Maven的标准布局
1.5 Maven常用基本概念1.5.1 Maven的LifeCycle有序的阶段(phase)集合,maven内置定义了三种LifeCycle:(可以以“就诊流程”作为类比,其中的“护士分诊”、“医师检验”等=phase,整个“就诊流程”可以看成是一个就诊的LifeCycle): 1) Default LifeCycle: <phases> <phase>validate</phase> <phase>initialize</phase> <phase>generate-sources</phase> <phase>process-sources</phase> <phase>generate-resources</phase> <phase>process-resources</phase> <phase>compile</phase> <phase>process-classes</phase> <phase>generate-test-sources</phase> <phase>process-test-sources</phase> <phase>generate-test-resources</phase> <phase>process-test-resources</phase> <phase>test-compile</phase> <phase>process-test-classes</phase> <phase>test</phase> <phase>prepare-package</phase> <phase>package</phase> <phase>pre-integration-test</phase> <phase>integration-test</phase> <phase>post-integration-test</phase> <phase>verify</phase> <phase>install</phase> <phase>deploy</phase> </phases> 2) Clean LifeCycle: <phases> <phase>pre-clean</phase> <phase>clean</phase> <phase>post-clean</phase> </phases> <default-phases> <clean> org.apache.maven.plugins:maven-clean-plugin:2.5:clean </clean> </default-phases> 3) Site LifeCycle: <phases> <phase>pre-site</phase> <phase>site</phase> <phase>post-site</phase> <phase>site-deploy</phase> </phases> <default-phases> <site> org.apache.maven.plugins:maven-site-plugin:3.3:site </site> <site-deploy> org.apache.maven.plugins:maven-site-plugin:3.3:deploy </site-deploy> </default-phases> 1.5.2 Maven的Plugin和Goalplugin是一组相关的Goal的集合(可以以“医院各专业科室”作为类比,检验科负责化验血尿等体液、放射科进行CT、X光检验):
图1?1 Plugins与Goal的关系 Maven的LifeCycle与Goal之间的关系:
图1?2 LifeCycle的phase与某plugin绑定的示意图 当packaging不同时,maven default生命周期绑定到不同的plugins的不同的goal上面。当packaging为jar时,maven default lifeCycle与各个plugins默认的绑定关系: 图1?3 packaging为jar时default LifeCycle的phases及pahase绑定的plugin和goal(部分phase) 1.5.3 Maven的坐标maven采用坐标来唯一定位一个组件,maven的坐标由以下几个部分组成。 1) groupId:公司、组织、项目组等,一般为倒置的域名,例如com.shentong; 2) artifactId: 在groupId下能唯一识别一个项目的标识符; 3) version:该ArtifactId发布的一个版本,对于正在开发的非稳定版本的Artifact,约定以SNAPSHOT作为后缀(“-SNAPSHOT”作为后缀,注意前面的“-”,并且SNAPSHOT全部大写)。 4) packaging:打包方式,有pom, jar,maven-plugin, ejb, war, ear, rar, par等 5) classifier 主要有以下两种用途: a) 附加到主要的artifact之上的附属组件,例如sources和javadoc; b) 由一个pom生成的不同文件,例如针对不同的JDK生成的不同的版本,如artifactId-version-jdk14.jar/artifactId-version-jdk15.jar 经常使用的GAV是坐标的简称,即G=GroupId,A=ArtifactId,V=Version),在上传组件到nexus服务器上面时经常用到。 1.5.4 dependency Scope依赖范围,主要用来控制2种情况: 1) 限制传递依赖; 2) 形成不同build类型(例如编译主代码、编译测试代码、运行测试代码)的不同的classpath。 有如下6种scope: 1) compile 默认的scope,该范围的依赖对所有的编译类型的classpath都有效,并且所有该依赖的依赖,也会作为该项目的compile类型的依赖; 2) provided 与compile类似,只会添加到compile和test时的classpath,并且不具有传递性;典型的如servlet-api.jar,编译时仍然需要从maven仓库中下载,但是运行时由容器例如tomcat提供; 3) runtime 表明在compile时不需要,只是在运行时需要。典型的如postgres JDBC驱动等通过反射调用的依赖; 4) test 表明在主代码编译和执行时并不需要,只是在单元测试代码编译和运行时需要,例如单元测试框架JUnit; 5) system 与provided类似,但是需要显示指定该依赖在本地文件系统中的位置,通过<systemPath>。system scope类型的依赖,maven在构建时不会从maven仓库中寻找,直接从指定的systemPath中寻找。典型如引用jre的tools.jar 6) import 只适用于dependency的type为pom类型的,不常用。暂不介绍,使用到时可参照maven 的reference。 表1?2 直接依赖的scope和传递依赖的scope不同组合的实际scope效果
1.5.5 Maven Repositorymaven仓库分类:
图1?4 Maven仓库分类 1.5.5.1 本地仓库在User setting配置文件中指定的<localRepository>节点的值所指向的位置。 1.5.5.2 远程仓库“远程仓库”是相对“本地仓库”来说的,不是“本地”的,就都是远程,不管是局域网还是互联网上的,远程仓库主要有如下几种类型: 1) maven的central仓库,在Super POM($M2_HOME\lib\maven-model-builder-x.x.jar\org\apache\maven\model\pom-4.0.0.xml)中定义; 2) 其他公共仓库: a) Java.net Maven的库; b) JBoss Maven库。 3) 私服:远程仓库的一种,采用maven仓库管理软件(例如nexus)在本地局域网搭建的maven仓库。 1.5.5.3 私服图1?5 maven私服部署图 私服的作用: 1) 节省网络带宽:每个组件只会从Internet上下载一次(首次请求时下载),然后,就保存在私服的缓存中了,团队其他成员后续的请求,直接从私服下载;没有互联网连接的时候,只要组件已经被私服缓存,就能够不访问Internet从私服下载组件; 2) 加速maven的构建:对SNAPSHOT版本不停的检查更新,会耗费较多的Internet网络流量; 3) 可部署第三方组件至私服,避免重复下载,例如oracle的JDBC驱动,公司内部的组件,如oscar的JDBC驱动和集群的驱动; 4) 可部署团队开发的产品组件至私服,避免组件在团队成员之间不规范的传递,特别是SNAPSHOT版本的传递。 如何配置项目使用其他仓库(非central仓库)? 1) 在pom.xml使用 <respositories> <respository> <id></id> <name></name> <url></url> <layout></layout> </respository> </ respositories > 声明; 2) 需要验证的远程仓库,要在settings.xml文件中使用 <servers> <server> <id></id> <user></user> <password></password> </server> </servers> 声明,并且保持id与pom.xml中声明的respository的id一致。
使用nexus私服来mirror central仓库,这样所有本应该从central仓库下载的组件,都直接从该mirror下载: <mirrors> <mirror> <!--Thissends everything else to /public --> <id>nexus</id> <mirrorOf>*</mirrorOf> <url>http://192.168.1.231:8080/nexus/content/groups/public</url> </mirror> </mirrors>
部署组件到仓库:在pom.xml文件中采用< distributionManagement >要部署的release和snapshot版本的组件分别到哪个仓库地址: <distributionManagement> <snapshotRepository> <id>nexus-snapshots</id> <name>Server231snapshots</name> url>http://192.168.1.231:8080/nexus/content/repositories/snapshots</url> </snapshotRepository> <repository> <id>nexus-release</id> <name>Server231releases</name> <url>http://192.168.1.231:8080/nexus/content/repositories/releases</url> </repository> </distributionManagement> 1.6 Maven常用命令和选项1) 编译源文件:mvn compile 2) 打包:mvn package 3) 安装:mvn install 4) 部署:mvn deploy 5) 执行单元测试:mvn test Dependency插件相关: 6) mvn dependency:sources 下载所有dependency的源码包,当然该源码包在repository中必须存在(Central或者是Nexus上面) 7) mvn dependency:resolve-Dclassifier=javadoc 下载所有dependency的docs包 …… 常用的option 1) 跳过单元测试,-DskipTests(省略了=true) 或者-Dmaven.test.skip=true 2) –U 强制检查artifact的SNAPSHOT的最新版本 3) –X输出调试信息 4) –e 输出错误信息 5) –P profilId使用profile 6) 定制组件的名字(想要与pom中的<name>不同时使用),例如-DfinalName=ABC,可以定制打包后文件的名字。 1.7 Maven的常用plugin1) maven-archetype-plugin:生成项目骨架,内置几百种archetype的骨架,帮你快速搭建标准项目结构; 2) maven-assembly-plugin:定制特殊需求的打包需求,支持定义的assembly格式,并支持团队共享; 3) maven-dependency-plugin:显示项目的实际依赖,进行依赖分析和优化; 4) maven-help-plugin:help:system可以打印所有可用的环境变量和Java系统属性。help:effective-pom和help:effective-settings最为有用,它们分别打印项目的有效POM和有效settings。mvnhelp:active-profiles可以显示当前活跃的profile,有时候切换了build环境之后,build失败,可以通过以上help插件的goals来确认当前生效的设置是否是如你期望的设置。help:evaluate可以以交互的形式让用户检查当前maven系统中变量的值,例如查看${project.version}、自定义property的值,尤其时再在使用profile时,由于pom中各个profile中变化的部分采用property定义,查看pom无法直观获得property值,而help:evaluate则提供了一个交互式查看工具。 5) maven-release-plugin:执行自动发布,重新checkout代码,或者在workingcopy中进行svn的tag/branch,指定release版本号,升级SNAPSHOT版本等; 6) maven-resources-plugin:将resources文件进行变量替换,并拷贝到classpath目录下; 7) maven-surefire-plugin:编译并执行单元测试; 参见:http://maven./surefire/maven-surefire-plugin/examples/single-test.html
执行单个测试类的所有方法: During development, you may run a singletest class repeatedly. To run this through Maven, set the test property to aspecific test case. mvn -Dtest=TestCircle test The value for the test parameter is thename of the test class (without the extension; we'll strip off the extension ifyou accidentally provide one). 可以使用通配符来执行多个单元测试类: mvn -Dtest=TestCi*le test And you may use multiple names/patterns,separated by commas: mvn -Dtest=TestSquare,TestCi*le test Running a set of methods in a Single TestClass With version 2.7.3, you can run only ntests in a single Test Class. NOTE : it's supported for junit 4.x andTestNG. You must use the following syntax mvn -Dtest=TestCircle#mytest test You can use patterns too mvn -Dtest=TestCircle#test* test As of surefire 2.12.1, you can selectmultiple methods (JUnit4X only at this time, patches welcome) mvn -Dtest=TestCircle#testOne+testTwo test 8) build-helper-maven-plugin:显示plugin的online帮助 9) exec-maven-plugin:执行可执行文件后者java代码; mvn exec:java -Dexec.mainClass="com.some.package.Main”–Dexec:args=“args0 args1” 使用如下命令可以显示exec:java goal支持多少种参数: mvn exec:help -Ddetail=true -Dgoal=java 10) maven-tomcat-plugin:进行war的远程部署和卸载,运行war等,eclipse中maven化的web工程调试就要用到tomcat:run;(对应的还有轻量级jetty服务器的maven-jetty-plugin) 11) maven-eclipse-plugin: 清理和生成eclipse的.project、.classpath文件;还可以生成myeclipse相关的项目文件。 12) maven-shade-plugin:fat jar的包的工具,功能比maven-assembly-plugin弱,但是配置简单。 13) maven-antrun-plugin:运行ant命令,适合与一些高度定制化的任务、尚没有对应的maven插件可以完成任务,例如与遗留系统的集成;常见的还有调用ant echo进行debug,输出一些属性的值。 14) build-helper-maven-plugin:可以为一个project添加多个source目录(一个pom中只能有一个sourceDirectory节点,造成一个项目只能有一个主src目录的假象),配置方法如下: <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>add-source</goal> </goals> <configuration> <sources> <source>src/plugin/parse-html/src/java</source> <source>src/plugin/parse-js/src/java</source> </sources> </configuration> </execution> </executions> </plugin> 所有的plugin列表以及reference文档,主要看plugin的配置、plugin常用的Goal、以及常用的参数: 1) http://maven./plugins/index.html 1.8 Maven进阶1.8.1 Multi-Module Project和Maven的继承机制项目复杂时,需要将项目拆分成多个子模块,能降低耦合,进行并行开发。maven通过<parent>和<module>来定义父子关系,parent的packaging定义成pom: <project> <modelVersion>4.0.0</modelVersion> <groupId>com.geni_sage</groupId> <artifactId>gdme</artifactId> <packaging>pom</packaging>
<modules> <module>xxx</module> <module>yyy</module> </modules> </project> 子类通过<parent>声明是谁的module: <parent> <groupId>com.geni_sage</groupId> <artifactId>gdme</artifactId> <version>5.0-SNAPSHOT</version> </parent> parent的所有的属性、dependency、plugins都默认被其<module>继承(除非<module>显式定义同名的,并且配置不同的属性值)。parent和module的关系没有层级限制,可以定义多层。 1.8.2 Maven的属性Maven有以下6类属性可以使用: 1) POM属性 使用${属性表达式} (propertyexpressions)来引用的变量(可以想象是类似于XML中xpath技术的pom.xml节点引用)常用的有: a) ${project.build.sourceDirectory}:项目主源码文件的根目录,默认是src/main/java/; b) ${project.build.testSourceDirectory}:测试源码文件的根目录,默认是src/test/java/; c) ${project.build.directory}:项目构建输出目录,默认是target/; d) ${project.outputDirectory}:项目主代码编译成.class文件的默认的输出目录,target/classes; e) ${project.testOuputDirectory}:项目测试代码.class文件的输出目录,默认为target/test-classes/; f) ${project.groupId}:项目的groupId; g) ${project.artifactId}:项目的artifactId; h) ${project.version}:项目的version,与${version}等价; i) ${project.build.finalName}:项目打包输出的文件名字,默认为${project.artifactId}-${project.version}.
2) 内置属性(特殊变量):
3) 定义在pom中的properties: <properties> <spring-version>2.5.6</ spring-version > </properties> 在复杂项目(多模块项目中)使用properties能够保证统一引用一个版本的artifact。 4) settings属性 与POM属性同理,使用以”settings.”开头的属性引用settings.xml文件中XML元素的值,常用的如${settings.localRepository},即本地maven仓库的地址。 5) Java系统属性 所有的java系统属性,常用的如${user.home}指向了用户目录,使用mvnhelp:system可以查看所有的java系统属性。 6) 系统环境变量: env.开头,可以直接引用环境变量,例如在使用<scope>system</scope>的时候,需要制定本地文件系统的文件路径,例如tomcat的lib目录中的servlet-api.jar,为了保证pom的可移植性,只需要引用${env.CATALINA_HOME}\lib\servlet-api.jar即可。 1.8.3 Maven的dependency优化1) 传递依赖的冲突解决——“最短路径优先”和“同等路径长度下,先定义的优先”原则; 2) 在parent中定义的<dependency>会被左右的<module>默认继承,该机制可以用来控制所有<module>都会用到或者大部分用到的组件进行统一控制,避免重复,和重复带来的不一致; 3) 使用<dependencyManagement>声明的<dependency>可以避免<module>自己随意定义dependency的版本,<module>定义的时候只需要声明<groupId>和<artifactId>即可,不用定义<version>。注意与< dependency>定义的不同,在parent当中使用< dependency >不管子类需不需要,都会添加到<module>的依赖中,但是使用<dependencyManagement>声明的依赖,子类可以按需引用,默认不会添加到<module>的依赖中; 4) 使用<properties>元素定义需要进行全局统一控制的一些列相关的版本号或者在多个<module>中用到的组件的版本号,例如spring,多处用到使用<spring.version>2.5.6</spring.version>; 5) 使用变量引用,能引用maven内置变量的地方,尽量引用,减少重复,方便修改; 6) 使用<eclusions>截断传递依赖,避免将不必要的依赖引入到最终的组件打包中。 1.8.4 Maven的plugin优化使用<plugins>和<pluginManagement>解决plugin继承和plugin按需继承的问题,原理同<dependencyManagement>和< dependency>的关系。 1.8.5 Maven的profileprofile目的是为了提高pom.xml的可移植性,针对不同的环境可以activate相应的环境的配置。 以一份代码,maven编译成功后,向京津两地的2个nexus服务器进行部署为例。 1.9 Maven与IDE1.9.1 m2eclipse插件需要注意的2个地方: 1) 使用自己下载最新版本的maven,而不要使用embedded maven 2) 2个配置的地方,installation和usersettings,保证effective setting是你预期的; 3) 切换新的workspace的时候,m2e又会默认使用embedded maven,需要重新设置。 常见问题: 1) m2e插件安装subversion的connector(可直接从SCM中import mavenproject) 2) 由于m2e插件自己的问题,报错“xxx is missing”,使用命令行试试,命令行如果能成功,再使用update dependency。 1.9.2 maven-eclipse-plugin如果非maven化之前,已经生成了.project、*.classpath, *.wtpmodules等与eclipse相关的项目文件,使用mvneclipse:clean先删除,后者手工删除,然后在通过命令行mvn eclipse:eclipse,自动生成这些文件,或者直接import exsiting maven project/import maven project from scm,都会自动生成这些文件(这也是不需要将这些文件提交到svn的原因)。 1.10 Maven vs ANT从一个简单的例子看ant与maven的区别: <project name="my-project"default="dist"basedir="."> <description> simple example build file </description> <!-- set globalproperties for this build --> <propertyname="src"location="src/main/java"/> <propertyname="build"location="target/classes"/> <propertyname="dist" location="target"/>
<targetname="init"> <!-- Createthe time stamp --> <tstamp/> <!-- Createthe build directory structure used by compile --> <mkdirdir="org.apache.maven.model.Build@6130fa43"/> </target>
<targetname="compile"depends="init" description="compile the source " > <!-- Compilethe java code from ${src} into org.apache.maven.model.Build@6130fa43 --> <javacsrcdir="${src}"destdir="org.apache.maven.model.Build@6130fa43"/> </target>
<targetname="dist"depends="compile" description="generate the distribution" > <!-- Createthe distribution directory --> <mkdirdir="${dist}/lib"/>
<!-- Puteverything in org.apache.maven.model.Build@6130fa43 into theMyProject-${DSTAMP}.jar file --> <jarjarfile="${dist}/lib/MyProject-${DSTAMP}.jar"basedir="org.apache.maven.model.Build@6130fa43"/> </target>
<targetname="clean" description="clean up" > <!-- Deletethe org.apache.maven.model.Build@6130fa43 and ${dist} directory trees --> <deletedir="org.apache.maven.model.Build@6130fa43"/> <deletedir="${dist}"/> </target> </project> ant clean/dist/compile分别执行清理、打包、编译。 同一个项目的pom文件: <project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
</project>
mvn install 会处理资源文件,编译源代码,运行单元测试,创建一个Jar包。
Ant 和 Maven 的区别是: Apache Ant: 1) Ant 没有正式的约定如一个一般项目的目录结构,你必须明确的告诉 Ant 哪里去找源代码,哪里放置输出。随着时间的推移,非正式的约定出现了,但是它们还没有在产品中模式化。 2) Ant 是过程式的,你必须明确的告诉 Ant 做什么,什么时候做。你必须告诉它去编译,然后复制,然后压缩。 3) Ant 没有生命周期(LifeCycle),你必须定义目标和目标之间的依赖。你必须手工为每个目标附上一个任务序列。 Apache Maven: 1) Maven 拥有约定,因为你遵循了约定,它已经知道你的源代码在哪里。它把字节码放到 target/classes ,然后在 target 生成一个 JAR 文件。 2) Maven 是声明式的。你需要做的只是创建一个 pom.xml 文件然后将源代码放到默认的目录。Maven 会帮你处理其它的事情。 3) Maven 有一个生命周期,当你运行mvn install 的时候被调用。这条命令告诉 Maven 执行一系列的有序的步骤,直到到达你指定的生命周期。遍历生命周期旅途中的一个影响就是,Maven 运行了许多默认的插件目标,这些目标完成了像编译和创建一个 JAR 文件这样的工作 1.11 Maven给我们带来了什么1) 显著降低了异地两地checkout代码的时间,未maven化之前,异地svn代码传输主要的时间耗费在jar包的传输上; 2) 为自动化构建(CI)提供了基础,为将开发人员进一步从保障性工作中解放出来,专注于算法和产品功能开发奠定了基础; 3) 提供了一套规范的编译和发布流程,并且将产品的组件在公司内部进行了集中式规范管理,无论是为以后多团队开发,或者是对外发布API开发包,都提供了一致的方案。 4) 为进一步理解和规范产品设计,执行模块重构和代码优化提供了基础。 1.12 FAQ1) 能否使用自定义的项目布局?即目录结构与标准的不同? 可以,但是需要重新设置plugin使用自定义的文件位置。不推荐这样做。 2) 在maven的localRespository中偶尔会出现.lastUpdate文件,出现该文件的时候可能会造成mvn命令行编译通过,但是eclipse的m2e插件却编译不通过,why?These filesindicate to Maven that it attempted to obtain the archive by download, but wasunsuccessful. In order to save bandwidth it will not attempt this againuntil a certain time period encoded in the file has elapsed. Thecommand line switch -U force maven to perform the update before the retryperiod. This may be necessary if you attempted to build while disconnectedfrom the network. |
|