Maven原理与实践一.maven简介1.1何为mavenMaven是一个采用纯Java编写的开源项目管理工具。Maven采用了一种被称之为projectobjectmodel(POM)概念来管理项目,所有的项目配置信息都被定义在一个叫做POM.xml的文件中,通过该文件,Maven可以管理项目的整个声明周期,包括编译,构建,测试,发布,报告等等。目前Apache下绝大多数项目都已经采用Maven进行管理。而Maven本身还支持多种插件,可以方便更灵活的控制项目。Maven是面向技术层面,针对Java开发项目管理工具,它提供了构建工具所提供功能的超集,除了构建功能之外,Maven还可以管理项目结构、管理依赖关系、生成报告、生成Web站点、有助于团队成员之间的交流与协作。1.2maven的优势指导开发:提供了Java项目的最佳开发实践,自由开发项目骨架而可自动生成项目结构。自动编译:不仅仅只像Ant自动编译,还包括测试,打包,发布,文档生成,项目站点生成……依赖管理:Maven可以方便地管理应用程序依赖,例如第三方依赖、模型依赖无限扩展:插件模式可以无限增强Maven功能,例如通过Tomcat、Jetty插件可以自由控制其服务器。持续集成:鼓励开发者积极提交代码,更早地发现程序错误,在并行开发中稳妥推进。开发协作:更简单和谐的团队协作1.3maven名词解释•Project:任何您想build的事物,Maven都可以认为它们是工程。这些工程被定义为工程对象模型(POM,PojectObjectModel)。一个工程可以依赖其它的工程;一个工程也可以由多个子工程构成。•POM:POM(pom.xml)是Maven的核心文件,它是指示Maven如何工作的元数据文件,类似于Ant中的build.xml文件。POM文件位于每个工程的根目录中。•GroupId:groupId是一个工程的在全局中唯一的标识符,一般地,它就是工程名。groupId有利于使用一个完全的包名,将一个工程从其它有类似名称的工程里区别出来。•Artifact:artifact是工程将要产生或需要使用的文件,它可以是jar文件,源文件,二进制文件,war文件,甚至是pom文件。每个artifact都由groupId和artifactId组合的标识符唯一识别。需要被使用(依赖)的artifact都要放在仓库(见Repository)中,否则Maven无法找到(识别)它们。•Dependency:为了能够build或运行,一个典型的Java工程会依赖其它的包。在Maven中,这些被依赖的包就被称为dependency。dependency一般是其它工程的artifact。•Plug-in:Maven是由插件组织的,它的每一个功能都是由插件提供的。插件提供goal(类似于Ant中的target),并根据在POM中找到的元数据去完成工作。主要的Maven插件要是由Java写成的,但它也支持用Beanshell或Ant脚本写成的插件。•Repository:仓库。二.maven原理简介Maven的主要组件如下图所示:1.2.1项目对象模型(POM)项目对象模型(ProjectObjectModel,POM)描述项目的各个方面。尽管对于POM的物理表示没有内在的限制,但Maven开发人员通常使用一个XML项目文件(project.xml)。该XML文件格式由位于Maven安装目录中的XML模式(maven-project.xsd)定义。通常,pom.xml文件由三个主要部分组成:项目管理部分包括项目的组织、开发人员名单、源代码位置和错误跟踪系统URL等信息。项目相关性部分包括关于项目相关性的信息。当前Maven实现(1.0beta测试版8)仅支持JAR文件相关性。项目构建和报告部分包含项目构建信息(如源代码目录、单元测试用例目录)和要在构建中生成的报告。清单1.主project.xml框架2.1.1项目管理部分项目管理部分(如清单2所示)主要包括可选项。在此部分中指定开发人员名单(带有正确的标识),当您希望获得更改日志(ChangeLog)报告和开发活动(DevelopmentActivity)报告时尤其要这么做。清单2.项目管理部分•一个Java构件的五大坐标元素:groupId:组IDartifactId:实际项目的IDversion:版本package:包类型,如JAR、EAR、POM…classifier:分类,如二进制包,源、文档通过这种规则就可以定位到世界上任何一个构件1.2.1.2项目相关性部分我们项目中用到的jar包可以通过依赖的方式引入,构建项目的时候从Maven仓库下载即可。清单3.项目相关性部分groupId告诉Maven资源库内哪个子目录中包含相关性文件。artifactId告诉Maven该构件的唯一标识。version表示相关性的版本号。jar(可选的)表示相关性的JAR文件。在绝大多数情况下,可以从相关性的artifactId和version构造JAR文件的名称。type(可选的)相关性的类型;如jar和分发版等。缺省值是jar。urlscope(可选的)相关性项目的URL,在相关性是在因特网上找到的第三方库时有用。(可选的)依赖的范围,下面会进行详解exclusions(可选的)用来排除传递性依赖,下面会进行详解1.依赖范围Maven有以下几种依赖范围:compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子就是JUnit,它只有在编译测试代码及运行测试的时候才需要。provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。systemPath元素可以引用环境变量。2.传递性依赖我们可以看到此项目引入依赖junit和spring-core,我们可以在Maven仓库中查找spring-core构件,如图:点击POM我们会看到该文件包含了一个commons-logging依赖:那么该依赖会传递到当前项目中,这就是依赖的传递性,打开项目查看Mavendependencies:3.可选依赖有时候我们不想让依赖传递,那么可配置该依赖为可选依赖,将元素optional设置为true即可,例如:那么依赖该项目的另一项目将不会得到此依赖的传递4.依赖调解第一原则:路径最短者优先,A-B-C-X(1.0),A-D-X(2.0),此时A只会依赖X(2.0)第二原则:第一声明者优先,当第一原则无法解决依赖冲突时,则按在pom中依赖声明的顺序决定5.排除依赖当我们引入第三方jar包的时候,难免会引入传递性依赖,有些时候这是好事,然而有些时候我们不需要其中的一些传递性依赖比如上例中的项目,我们不想引入传递性依赖commons-logging,我们可以使用exclusions元素声明排除依赖,exclusions可以包含一个或者多个exclusion子元素,因此可以排除一个或者多个传递性依赖。需要注意的是,声明exclusions的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。换句话说,Maven解析后的依赖中,不可能出现groupId和artifactId相同,但是version不同的两个依赖。如下是一个排除依赖的例子:6.依赖归类如果我们项目中用到很多关于SpringFramework的依赖,它们分别是org.springframework:spring-core:2.5.6,org.springframework:spring-beans:2.5.6,org.springframework:spring-context:2.5.6,它们都是来自同一项目的不同模块。因此,所有这些依赖的版本都是相同的,而且可以预见,如果将来需要升级SpringFramework,这些依赖的版本会一起升级。因此,我们应该在一个唯一的地方定义版本,并且在dependency声明引用这一版本,这一在SpringFramework升级的时候只需要修改一处即可。1.2.1.3项目构建部分项目构建和报告部分(如清单4所示)包含用于配置某些Maven插件的重要构建和报告信息。例如,可以配置Maven在站点文档生成时包含还是排除某些报告。清单4.项目构建部分1.编译源代码:mvncompile2.编译测试代码:mvntest-compile3.运行测试:mvntest4.产生site:mvnsite5.打包:mvnpackage6.在本地Repository中安装jar:mvninstall7.清除产生的项目:mvnclean8.生成eclipse项目:mvneclipse:eclipse9.生成idea项目:mvnidea:idea10.组合使用goal命令,如只打包不测试:mvn-Dtestpackage11.编译测试的内容:mvntest-compile12.只打jar包:mvnjar:jar13.只测试而不编译,也不测试编译:mvntest-skippingcompile-skippingtest-compile(-skipping的灵活运用,当然也可以用于其他组合命令)14.清除eclipse的一些系统设置:mvneclipse:clean三.资源库在以前使用Ant的时候,我们会建立一个lib目录在存放我们的jar包,比如项目所依赖的第三方包,每建立一个项目都要建立一个lib,不停的做copy工作,不仅是对于磁盘的浪费,而且也造成了版本管理上的麻烦。而且我们还需要通过提交到svn上来对lib进行管理,但是svn对于这种二进制文件的管理并不出色。Maven仓库的初中就是为了解决这个问题,是所有常用的第三方包的集中营。这样所有的Maven项目就可以从这个仓库中获取所需要的资源,Maven仓库中对jar通过GroupId,AtifactId,version来管理,所以Maven项目可以很方便的进行依赖管理。你不需要自己来管理这个庞大的资源仓库,当然你可以创建一个公司层面的仓库管理器,这个我在这个章节的后面会介绍。Maven仓库的两个概念:本地仓库和远程仓库本地仓库是远程仓库的一个缓冲和子集,当你构建Maven项目的时候,首先会从本地仓库查找资源,如果没有,那么Maven会从远程仓库下载到你本地仓库。这样在你下次使用的时候就不需要从远程下载了。如果你所需要的jar包版本在本地仓库没有,而且也不存在于远程仓库,Maven在构建的时候会报错,这种情况可能发生在有些jar包的新版本没有在Maven仓库中及时更新。Maven缺省的本地仓库地址为${user.home}/.m2/repository。也就是说,一个用户会对应的拥有一个本地仓库