[原创]Maven实战-读书笔记
Maven读书笔记
使用Jetty-maven-plugin对web应用进行测试
版本管理
依赖
-
快捷键:Alt+Shift+X M
在Eclipse中运行Maven命令的快捷方式是:Alt+Shift+X M。按下Alt+Shift+X之后,从弹出的菜单中可以看到,选择R是:run on server。所以运行于服务器的快捷键是:Alt+Shift+X R
-
使用ArcheType来创建Maven项目骨架:
mvn archetype:generate
-
Maven中坐标的定义
以nexus-indexer为例:
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>
groupId:maven项目(模块)的上一级项目,比如nexus-indexer属于nexus这个项目,这里的命名使用和Java中包名类似的方式,域名的倒写。
artifactId:maven模块的名字,一般以上级项目作为前缀,比如这里的:nexus-indexer
version:版本号
packaging(可选的):默认是jar,可以指定为war或jar
还有一个坐标:classifier,用以描述附属构件,不能直接定义它,它是由附加的插件帮助生成的。例如可以通过一些插件来生成nexus-indexer模块的javadoc和sources,这些属于附属构件。
项目构件的文件名与坐标名是对应的,一般的规则是:artifactId-version[-classifier].packaging。所以nexus-indexer的主构件文件名可以是:nexus-indexer-2.0.0.jar,而附属构件可以是:nexus-indexer-2.0.0-javadoc.jar。packaging不一定就是文件扩展名,比如packaging为maven-plugin的构件的扩展名是jar。
-
依赖范围
Maven在编译(compile),编译并执行测试(test),运行(runtime)这三种环境下使用三种不同的classpath,依赖范围就是用来控制这三种classpath的。有六种依赖范围:
-
compile:默认的依赖范围。对编译,测试,运行三种classpath都有效;
-
test:只对测试classpath有效,典型的例子是Junit;
-
runtime:对测试和运行classpath有效,典型的例子是JDBC驱动的实现,编译的时候只需要JDK提供的JDBC接口,不需要实现。
-
provided:只对编译和测试有效,运行时无效。典型的例子是servlet-api,因为在运行时,容器必然会提供这些接口,不需要再包含。
-
system:它和provided一样,不过它依赖的包不是通过Maven仓库获取,而是指定依赖文件的路径。
-
import:导入依赖范围。它不对classpath造成影响。
-
依赖传递
A依赖B,B依赖C,那么A可能会间接依赖到C,规律是这样的:
-
如果B对C的依赖范围是Compile,那么A对C的依赖和A对B的依赖是一样的。
-
如果B对C的依赖范围是Test,那么A不依赖于C
-
如果B对C的依赖范围是runtime,那么A如果对B的依赖范围是Compile,那么A对C的依赖范围是Runtime,其它情况下,B对C的依赖都会传递给A。
-
如果B对C的依赖范围是provided,那么只传递B对C的provided依赖给A;
如下表所示(最上面一行表示B对C的依赖范围,最左侧一列表示A对B的依赖范围,中间的单元格表示A对C的依赖范围):
|
compile |
test |
runtime |
provided |
|
|
compile |
compile |
- |
runtime |
- |
|
runtime |
runtime |
- |
runtime |
- |
|
test |
test |
- |
test |
- |
|
provided |
provided |
- |
provided |
provided |
-
依赖调解
依赖调解遵循下面的两个原则:
-
路径最近这优先:比如,A->B->C->X(1.0),,A->D->X(2.0),此时A依赖于X(2.0)
-
路径相同时,第一声明者优先:比如,A->B->Y(1.0),A->C->Y(2.0),此时根据在Pom文件中声明的顺序来决定哪个版本的Y被解析使用。
-
可选依赖
如果A依赖B,而B对X和Y的依赖都是可选的,那么按照Maven的规则,A就不会依赖于X和Y,但是实际运行的时候,A其实是需要X或Y的,所以在A中需要显示的声明对X或Y的依赖。为什么会出现这种情况呢?举个例子就明白了,例如B实现某个特性,既可以基于MySQL数据库,也可以基于postgresql数据库,这两个依赖对B都是可选的,但是当A使用B的时候,必须确定一种底层数据库实现,要么选择MySQL,要么选择postgresql。
不推荐使用可选依赖。最好的做法是,B构建两个Jar包,分别用于支持MySQL和postgresql。这也是软件设计里面的单一职责原则。
-
最佳实践
-
排除依赖:排除依赖的理由就是不想引入间接依赖的某个Jar包,而使用另一个等价的Jar包替换之(可能的原因有:间接依赖引入的是一个SNAPSHOT--不稳定版本,需要使用稳定版本替代之,或者是简介依赖引入的是一个受版权保护的版本,在Maven中央仓库里面没有该Jar包,需要用开源版本替换之)
-
为一类依赖统一定义版本:比如account-email项目依赖于springframework-core,springframework-context,springframework-context-support,springframework-beans,它们都是同一个版本,可以定义一个变量来保存版本:
<properties>
<springframework.version>2.5.6</springframework.version>
</properties>
定义依赖的时候,引用该版本:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
-
分析依赖。可以使用这些命令来分析依赖:
-
mvn dependency:list:列出所有的依赖包
-
mvn dependency:tree: 列出依赖关系树
-
mvn dependency:analyze: 分析声明但未使用的依赖和使用但未声明的依赖。
Maven仓库
-
构件的概念
Maven中,任何一个依赖、插件或者项目构建的输出,都可以成为构件。
-
构件在Maven仓库中的路径
构件在Maven仓库中的路径分成如下几个部分:
-
基于groupId的准备路径
-
基于artifactId的准备路径
-
基础版本信息:所谓基础版本是指在SNAPSHOT版本中,取除SNAPSHOT以外的版本信息,比如1.0-SNAPSHOT的基础版本是1.0。
-
构件的文件名:{artifactId}-{version}-{classfier}.{extension},这里的version是整个版本信息,不是基础版本信息。
例如这个构件:groupId=org.testng artifactId=testng version=5.8-SNAPSHOT classfier=jdk15 packaging=jar 它的Maven仓库路径为:
org/testng/testng/5.8/testng-5.8-SNAPSHOT-jdk15.jar
-
Maven仓库
Maven仓库分成下面几类:
-
按地理位置分为:本地仓库和远程仓库
-
远程仓库又分为:中央仓库、私服和其它公共库。
本地仓库:
Maven在编译和测试时,总是使用本地仓库中的依赖文件。一个构件进入本地仓库有两种途径:
-
从远程仓库下载到本地仓库
-
将本地构件安装到Maven仓库中:mvn clean install
远程仓库:
本地仓库好比是自己的书房,远程仓库好比是书店(比如amazon),要想看一本书,需要把书从远程仓库收藏到自己的本地仓库中。
中央仓库:
中央仓库就是默认的远程仓库。中央仓库的定义位于{$MAVEN_HOME}/lib/maven-model-builder-3.0.4.jar这个包里面的:\org\apache\maven\model\pom-4.0.0.xml文件中,这个POM文件也是所有Maven项目都会继承的超级POM。
私服:
私服是架设在局域网内的仓库服务,它代理广域网上的远程仓库。局域网内的用户向私服请求构件,如果私服上不存在,则从外部的远程仓库上下载,缓存到私服上之后,再为局域网内的下载请求提供服务。也可以将构件安装到私服,一共团队内使用。
私服的架构如下所示:
搭建私服有下列的好处:
-
节省外网带宽
-
加速Maven构建:访问局域网内的私服速度要快得多
-
部署第三方构件:组织内部生成的私有构件需要安装在私服上供团队内使用,而且一些受版权保护的构件(比如Oracle的JDBC驱动)也不会出现在中央仓库上,这些需要安装到私服上。
-
增强稳定性,增强控制。
-
减轻对中央仓库的压力:这是从中央仓库的角度来说的,Maven鼓励建立私服,而不是让所有开发人员都直接访问中央仓库。
-
远程仓库的配置
从超级POM文件中可以看到,默认的中央仓库的配置信息如下所示:
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>http://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
可以看到,一个远程仓库的配置在<repositories>元素的<repository>子元素下面,下面的属性需要说明:
-
id:每一个仓库声明的id都必须是唯一的,默认的中央仓库的id为central,如果其它仓库声明也是用该id,那么会覆盖中央仓库的配置。
-
url:指向仓库的地址,在浏览器中可以通过该url访问构件
-
layout:值default表示使用maven2和maven3的布局,而不是用maven1的布局
-
snapshots:enabled值为false表示,关闭中央仓库对快照版本下载的支持。与这个属性类似的一个属性是releases,它也有eabled属性,值true表示支持发布版本的下载。releases的定义是这样的:
<releases>
<enabled>true</enabled>
</releases>
对于releases和snapshots,除了enabled之外,还有两个属性:updatePolicy和checksumPolicy,分别控制从远程仓库检查更新和校验文件的策略,具体值如下表所示:
|
属性 |
取值 |
说明 |
|
updatePolicy |
never |
从不检查更新 |
|
daily |
默认值。每天检查一次更新。 |
|
|
always |
每次构建都检查更新 |
|
|
interval:X |
每个X分钟检查一次更新 |
|
|
checksumPolicy |
warn |
默认值。校验失败时输出警告信息。 |
|
fail |
校验失败时,构件的构建会失败。 |
|
|
ignore |
忽略校验错误。 |
-
远程仓库的认证
在settings.xml中配置仓库的认证信息,如下所示:
<settings>
……
<servers>
<server>
<id>deploymentRepo</id>
<username>repouser</username>
<password>repopwd</password>
</server>
</servers>
……
</settings>
其中的id属性必须和POM文件中配置的repository元素的id完全一致(见上一节里面配置的repository)
-
部署至远程仓库
往往日常开发的构件需要部署到远程仓库中,需要在项目的POM文件中,配置distributionManagemant元素,如下所示:
<project>
……
<distributionManagement>
<repository>
<id>proj-release</id>
<name>proj release repository</name>
<url>http://192.168.1.100/repositories/proj-releases</url>
</repository>
<snapshotRepository>
<id>proj-snapshot</id>
<name>Proj Snapshot Repository</name>
<url>http://192.168.1.100/content/repositories/proj-snapshots</url>
</snapshotRepository>
</distributionManagement>
……
</project>
使用命令:mvn clean deploy 将构件部署到远程仓库。Maven会判断出当前构件时快照版本还是发布版本。
-
快照版本
什么是快照版本?
类似2.1-SNAPSHOT、2.1-20091214-1413-13这种版本就是快照版本,在将快照版本发布到私服的过程中个,Maven会自动为构件打上时间戳,比如,20091214-1413-13就表示2009年12月14日,14点13分的第13个版本。有了时间戳,Maven就能找到2.1-SNAPSHOT快照版本的最新文件。默认情况下,Maven每天检查一次快照版本的更新。可以使用-U参数(比如,mvn clean install -U )来强制更新构件。
为什么需要快照版本?
在团队内部,当一个模块的开发还未成熟,其它开发人员又需要使用该构件时,可以使用快照版本来解决依赖问题。因为不稳定的Jar包需要频繁更新,如果不使用快照,而是用发布版本,那么需要经常修改POM文件中的版本号,或者重复部署同样的版本号供Maven下载--这需要清除本地仓库才能使Maven下载同一个版本的构件。
在什么情况下使用快照版本?
快照版本只能用于组织内部。项目不应该依赖于任何组织外部的快照版本,因为快照版本是不稳定的,随时可能会更新,这样会导致项目的构建今天是成功的,而由于外部快照版本的变化,以后的构建却不一定成功。
-
从仓库中解析依赖的策略
总体的原则是这样的:
-
如果本地仓库中没有需要的构件,则从远程仓库下载
-
当依赖版本为SNAPSHOT时,Maven自动找到最新的快照
-
当依赖版本为RELEASE或LATEST时,Maven会计算出最新的发布版本和最新的版本(包括快照版本)的值,然后在本地仓库或远程仓库中查找该版本的构件。不推荐使用RELEASE和LATEST,因为它们带有不确定性,构件的发布版本或最新版本发生变化时,使用者不会得到通知,所以出错时,往往不方便定位问题。
-
镜像
如果仓库X可以提供仓库Y中存储的所有内容,则认为X是Y的一个镜像。在settings.xml中配置镜像:
<mirrors>
<mirror>
<id>internal-repository</id>
<name>Internal Repository Manager</name>
<url>http://192.168.1.100/maven2</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
可以将私服配置为所有外部公共仓库的镜像,因为私服可以代理所有的公共仓库。
-
仓库搜索服务
可以在这些网站搜索Jar包:
生命周期和插件
-
概念
生命周期
Maven对软件构建的整个生命周期做了定义,这个定义基本能满足所有软件项目构建的需求,包括:项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等。生命周期的每一个阶段都由插件来实现。Maven为每一个阶段关联了默认插件。比如针对编译的插件有:maven-compiler-plugin,针对测试的插件有:maven-surefire-plugin等。
Maven中有三套生命周期:clean、default、site。每个生命周期都有自己的阶段,生命周期之间是独立的。具体的生命周期可以不用管,只要知道使用下面的命令就可以了:
-
$mvn clean:执行clean生命周期的clean阶段
-
$mvn test:执行default生命周期的test阶段
-
$mvn clean install:执行clean生命周期的clean阶段,然后执行default生命周期的install阶段
插件目标
Maven只定义了抽象的生命周期,具体的功能由插件来完成。每个插件往往会实现多个功能,这些功能就成为插件目标。比如,maven-dependency-plugin这个插件的目标有:dependency:analyze、dependency:tree、dependency:list等。maven-compiler-plugin插件有compiler:compile目标,maven-surefire-plugin有surefire:test目标。(surefire: [ˈʃʊəˈfaɪə]
)
生命周期阶段与插件的绑定
Maven的生命周期与插件目标相互绑定,以完成实际的构建任务。如下图所示:
Maven默认为主要的生命周期阶段都绑定了插件,下表是default生命周期与插件的绑定关系:
|
生命周期阶段 |
插件目标 |
执行任务 |
|
process-resources |
maven-resources-plugin:resources |
复制主资源文件至主输出目录 |
|
compile |
maven-compiler-plugin:compile |
编译主代码至输出目录 |
|
process-test-resouces |
maven-resources-plugin:testResources |
复制测试资源至测试输出目录 |
|
test-compile |
maven-compiler-plugin:compiler |
编译测试代码至测试输出目录 |
|
test |
maven-surefire-plugin:test |
执行测试用例 |
|
package |
maven-jar-plugin:jar |
创建项目Jar包 |
|
install |
maven-install-plugin:install |
将项目输出构件安装到本地仓库 |
|
deploy |
maven-deploy-plugin:deploy |
将项目输出构件部署到远程仓库 |
default生命周期中的一些阶段没有绑定任何插件,因此没有任何实际行为。
手工绑定生命周期阶段与插件目标
下面的例子演示了手工将maven-source-plugin的jar-no-fork目标与default生命周期的verify阶段绑定,用以创建项目的源码jar包:
<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>
-
插件配置
可以通过命令行和POM文件来配置插件的参数;
-
命令行方式:$mvn install -Dmaven.test.skip=true
-
POM文件方式:
<build>
<plugins>
……
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
……
</plugins>
</build>
-
获取插件信息
从下面站点可以获取插件信息,以了解插件有哪些可配置点:
也可以使用mvn:help命令来了解某一插件的信息:
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin:2.1
可以直接用目标前缀替代adgroupId:artifactId:version,如下所示:
mvn help:describe -Dplugin=compiler
带上-Ddetail可以看到详细信息:
mvn help:describe -Dplugin=compiler -Ddetail
输出结果中列举出了该插件的目标,也给出了目标前缀,方便了在命令行执行目标:
Name: Maven Compiler Plugi
Description: The Compiler Plugin is used to compile the sources of your project.
Group Id: org.apache.maven.plugins
Artifact Id: maven-compiler-plugin
Version: 2.1
Goal Prefix: compiler
This plugin has 3 goals:
compiler:compile
Description: Compiles application sources
compiler:help
Description: Display help information on maven-compiler-plugin.
Call
mvn compiler:help -Ddetail=true -Dgoal=<goal-name>
to display parameter details.
compiler:testCompile
Description: Compiles application test sources.
For more information, run 'mvn help:describe [...] -Ddetail'
-
插件前缀
插件前缀和groupId:artifactId是一一对应的,这种匹配关系存储在仓库元数据中,位于groupId/mavenmetadata.xml中,这里的groupId是公共插件仓库的默认groupId,有两个取值:org.apache.maven.plugins和org.codehaus.mojo,也可以是自定义插件仓库的groupId。执行插件目标的时候,Maven会根据插件前缀找到对应的插件信息。
聚合与继承
-
聚合与继承
聚合与继承都是为了方便管理项目,更好的在Maven中应用软件编程的优秀思想。聚合,是将多个Maven项目聚合到一个项目中,便于统一编译,测试,打包等构建操作。继承,是将面向对象的继承思想应用到项目组织里面,使用继承可以减少重复配置,使项目之间的结构更合理。聚合与继承的功能各不相同,但是可以将父模块和聚合模块合并成一个模块。
聚合项目其实是一个空壳Maen项目,它只有一个POM文件,该POM的内容为:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-aggregator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Aggregator</name>
<modules>
<module>account-email</module>
<module>account-persist</module>
<module>account-parent</module>
</modules>
</project>
被继承的项目作为其它项目的父项目,可以集中定义一些公用的依赖,变量,以及groupId,version。子项目中不需要再定义这些内容。父项目的例子:
……
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Parent</name>
<properties>
<springframework.version>2.5.6</springframework.version>
<junit.version>4.7</junit.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
……
红色的内容都是可以被子项目继承的部分。父项目使用<dependencyManagement>来管理依赖,dependencyManagement中定义的依赖不会为子项目增加依赖,但是它可以限制项目中依赖包的版本号和范围。比如,在子项目中只需这样配置就行了:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
……
</dependencies>
子项目中不需要声明依赖的版本号,也不需要声明依赖的范围。使用dependencyManagement可以统一管理项目中依赖的版本号。同理pluginManagement也可以用来管理插件。
-
导入依赖
dependencyManagement可以用来导入范围为import的依赖。如果有多个项目,它们的依赖版本基本是一致的,就可以定义一个使用dependencyManagement专门管理依赖的POM,然后在各个项目中导入这些依赖,子项目中的配置如下所示:
<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>
-
反应堆
反应堆是指,在多模块的Maven项目中,所有模块组成的一个构建结构。对于单模块项目,反应堆就是该模块本身,但是对于多模块项目,反应堆需要计算各模块之间的继承与依赖关系,以便确定出合理确的构建顺序。
反应堆的构建顺序是这样的:Maven按顺序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块。
裁剪反应堆
用下面的命令可以指定只构建反应堆中的部分模块,而不是整个反应堆都构建:
-pl 指定构建的模块,用逗号隔开多个模块:
mvn clean install -pl account-email,account-persist
-am 同时构建所列模块的依赖模块:
mvn clean install -pl account-email -am
-amd同时构建依赖于所列模块的模块
mvn clean install -pl account-email -amd
-
用Nexus创建私服
-
下载安装
Nexus有免费版和专业版,免费版的下载地址是:http://www.sonatype.org/nexus/go
这是直接解压就能运行的,它自带jetty容器。还有一种war包形式发布的版本,需要放在容器里面才能运行。也在同一个页面下载。
解压后,可以按下面的方式运行:
nexus-2.3.1-01\bin\nexus.bat -start
然后在浏览器里面访问:
管理员的默认用户名和密码分别是:admin/admin123
Nexus仓库的分类
-
hosted:私服本地的仓库
-
proxy:代理的远程仓库
-
group:仓库组,它把多个本地和远程仓库合并在一起
-
virtual:不用管
下载远程仓库的索引
需要手工开启对远程仓库索引的下载,选择一个仓库,然后在属性里配置:
配置本机从私服下载构件
<settings>
<!--配置镜像,所有从远程仓库获取的依赖,都从私服获取-->
<mirrors>
<mirror>
<id>central</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/nexus/content/groups/public/</url>
</mirror>
</mirrors>
<!--使用profile配置私服,配置id为central的仓库,覆盖超级POM中的配置-->
<profiles>
<profile>
<id>JDK1.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>
<profile>
<id>central</id>
<repositories>
<repository>
<id>central</id>
<url>http://central</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://central</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>central</activeProfile>
<activeProfile>JDK1.6</activeProfile>
</activeProfiles>
</settings>
部署构件至私服
在项目的POM文件中需要做如下配置:
<project>
<distributionManagement>
<repository>
<id>nexus-releases</id>
<name>Nexus Releases Repository</name>
<url>http://localhost:8081/nexus/content/repositories/releases</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<name>Nexus Snapshots Repository</name>
<url>http://localhost:8081/nexus/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
</project>
配置服务器认证信息:
<servers>
<server>
<id>releases</id>
<username>admin</username>
<password>iapppay</password>
</server>
<server>
<id>snapshots</id>
<username>admin</username>
<password>iapppay</password>
</server>
</servers>
-
使用Maven进行测试
测试类的模式
测试类满足下面的命名模式才会被maven-surefire-plugin作为测试类来执行:
-
**/Test*.java
-
**/*Test.java
-
**/*TestCase.java
以Tests结尾的测试类不会被Maven执行。
使用参数
可以使用-DskipTests来跳过测试:
$mvn package -DskipTests
也可以使用-Dtest参数来限定需要执行的测试:
$mvn package -Dtest=RandomGeneratorTest
Maven还支持在-Dtest参数中指定更高级的正则表达式:
$mvn package -Dtest=Random*Test
也可以指定多个测试用例,测试用例之间用逗号分隔:
$mvn package -Dtest=RandomGeneratorTest,AccountCaptchaServiceTest
测试报告
测试报告位于:target\surefire-reports目录下,有两种形式的报告:txt格式和XML格式。
测试覆盖率
通过Mavne的Cobertura插件可以检测出代码的测试覆盖率:
$mvn cobertura:cobertura
测试覆盖率报告位于:target\site\cobertura目录下
打包测试代码
通过配置maven-jar-plugin插件可以打包测试代码:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
这样在运行 $mvn package后,会生成一个测试代码的Jar包,比如:
account-captcha-0.0.1-SNAPSHOT-tests.jar
-
使用Hudson进行持续集成
概念
持续集成想要达到的目的:
-
快速集成
-
高频率自动集成
-
自动构建:编译、测试、打包、部署、审查
-
集成团队所有成员的最新源码
-
集成结果及时反馈给开发人员
-
集成结果自动部署到私服中
一个典型的持续集成场景如下图所示:
一次完整的集成往往包括以下6个步骤:
-
持续编译:CI服务器按一定频率检查源码控制系统,如果有新代码就触发一次集成
-
持续数据库集成:每次发现新的SQL脚本,就清理集成环境的数据库,重新创建表结构,并填入预备的数据。
-
持续测试:每次集成式运行自动化测试
-
持续审查:审查测试覆盖率报告,CheckStyle报告,PMD报告等
-
持续部署
-
持续反馈
持续集成带来的好处:
-
尽早暴露问题
-
减少重复操作
-
简化项目发布
-
建立团队信心:因为持续集成会覆盖大部分项目中的问题
Hudson
Hudson比较容易安装和运行。具体的配置可以参考第11章 “使用Hudson进行持续集成”。
-
使用Maven构建Web应用
一个典型的War文件的目录结构是:
METE-INF目录存放打包元数据信息,一般不用管
WEB-INF目录下必须包含一个Web资源表述文件web.xml,
classes和lib都会在运行时加入到classpath中
Maven打包的War文件和jar文件的区别在于,war文件有一个web资源目录,其默认位置是:src/main/webapp,一个典型的Web项目的Maven结构如下所示:
Maven打包的War包结构和通常的War包结构有一个对应关系,在src/main下有一个webapp目录,该目录下有web.xml,jsp,html,js,css等文件和目录。
可以使用<build>标签的finalName属性来指定最终打的war包的名字:
<build>
<!-- 指定一个简洁的war包名字 -->
<finalName>account</finalName>
……
</build>
-
使用Jetty-maven-plugin对web应用进行测试
有一个大原则:
-
可以用单元测试覆盖的代码就不应该依赖于Web页面测试
-
web页面测试仅限于对JSP,CSS,JS的修改,其它代码都应该用单元测试覆盖
jetty-maven-plugin可以把我们对代码的修改及时部署到测试服务器上,我们可以实时测试对代码的修改,这可以加速开发。可以像下面这样启动jetty:
$mvn jetty:run -Djetty.port=9999
如果不指定jetty.port,默认是8080端口。
不过,因为只有adgroupId是org.apache.maven.plugins和org.codehauls.mojo的插件才支持简化的命令行调用,比如mvn help:system这种。jetty的groupId是org.mortbay.jetty,要想直接使用jetty:run,还需要在maven的settings.xml文件中作如下的配置:
<settings>
<pluginGroups>
<!--配置jetty-maven-plugin-->
<pluginGroup>org.mortbay.jetty</pluginGroup>
</pluginGroups>
</settings>
在POM文件中需要对Jetty的参数做配置:
<pluginManagement>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.1.0.RC1</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webAppConfig>
<contextPath>/account</contextPath>
</webAppConfig>
</configuration>
</plugin>
</plugins>
</pluginManagement>
经过试用,jetty:run确实很方便,修改jsp,或js代码之后,不需要重新构建,打包,部署,直接就能看到效果,加快了开发的速度。
-
使用Cargo实现自动化部署
cargo可以帮助实现在各种容器下的自动部署。
cargo的主页:http://cargo.codehaus.org/Home
-
版本管理
下图可以说明快照版本和发布版本之间的演变:
版本的含义,以1.3.4-beta-2为例:
1:表示该版本是第一个重大版本
3:表示这是基于重大版本的第三个次要版本
4:表示该次要版本的第4个增量
beta-2:表示该增量的某一个里程碑
<主版本>.<次版本>.<增量版本>-<里程碑版本>
-
灵活的构建
-
生成项目站点
2013年4月12日0:13:42 完
posted on 2013-04-28 14:42 seeker2012 阅读(604) 评论(0) 收藏 举报
浙公网安备 33010602011771号