seeker2012

导航

[原创]Maven实战-读书笔记

Maven读书笔记





Maven读书笔记

依赖

快捷键:Alt+Shift+X M

使用ArcheType来创建Maven项目骨架:

Maven中坐标的定义

依赖范围

依赖传递

依赖调解

可选依赖

最佳实践

Maven仓库

构件的概念

构件在Maven仓库中的路径

Maven仓库

本地仓库:

远程仓库:

远程仓库的配置

远程仓库的认证

快照版本

什么是快照版本?

为什么需要快照版本?

在什么情况下使用快照版本?

从仓库中解析依赖的策略

镜像

仓库搜索服务

生命周期和插件

概念

生命周期

插件目标

生命周期阶段与插件的绑定

手工绑定生命周期阶段与插件目标

插件配置

获取插件信息

插件前缀

聚合与继承

聚合与继承

导入依赖

反应堆

裁剪反应堆

用Nexus创建私服

下载安装

Nexus仓库的分类

下载远程仓库的索引

配置本机从私服下载构件

部署构件至私服

使用Maven进行测试

测试类的模式

使用参数

测试报告

测试覆盖率

打包测试代码

使用Hudson进行持续集成

概念

Hudson

使用Maven构建Web应用

使用Jetty-maven-plugin对web应用进行测试

使用Cargo实现自动化部署

版本管理


依赖

  1. 快捷键:Alt+Shift+X M

在Eclipse中运行Maven命令的快捷方式是:Alt+Shift+X M。按下Alt+Shift+X之后,从弹出的菜单中可以看到,选择R是:run on server。所以运行于服务器的快捷键是:Alt+Shift+X R

  1. 使用ArcheType来创建Maven项目骨架:

mvn archetype:generate

  1. 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。

  1. 依赖范围

Maven在编译(compile),编译并执行测试(test),运行(runtime)这三种环境下使用三种不同的classpath,依赖范围就是用来控制这三种classpath的。有六种依赖范围:

  • compile:默认的依赖范围。对编译,测试,运行三种classpath都有效;

  • test:只对测试classpath有效,典型的例子是Junit;

  • runtime:对测试和运行classpath有效,典型的例子是JDBC驱动的实现,编译的时候只需要JDK提供的JDBC接口,不需要实现。

  • provided:只对编译和测试有效,运行时无效。典型的例子是servlet-api,因为在运行时,容器必然会提供这些接口,不需要再包含。

  • system:它和provided一样,不过它依赖的包不是通过Maven仓库获取,而是指定依赖文件的路径。

  • import:导入依赖范围。它不对classpath造成影响。


  1. 依赖传递

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



  1. 依赖调解

依赖调解遵循下面的两个原则:

  • 路径最近这优先:比如,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被解析使用。

  1. 可选依赖

如果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。这也是软件设计里面的单一职责原则。

  1. 最佳实践

  • 排除依赖:排除依赖的理由就是不想引入间接依赖的某个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仓库

  1. 构件的概念

Maven中,任何一个依赖、插件或者项目构建的输出,都可以成为构件。

  1. 构件在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

  1. Maven仓库

Maven仓库分成下面几类:

  • 按地理位置分为:本地仓库和远程仓库

  • 远程仓库又分为:中央仓库、私服和其它公共库。

本地仓库:

Maven在编译和测试时,总是使用本地仓库中的依赖文件。一个构件进入本地仓库有两种途径:

  1. 从远程仓库下载到本地仓库

  2. 将本地构件安装到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。

私服:

私服是架设在局域网内的仓库服务,它代理广域网上的远程仓库。局域网内的用户向私服请求构件,如果私服上不存在,则从外部的远程仓库上下载,缓存到私服上之后,再为局域网内的下载请求提供服务。也可以将构件安装到私服,一共团队内使用。

私服的架构如下所示:

搭建私服有下列的好处:

  1. 节省外网带宽

  2. 加速Maven构建:访问局域网内的私服速度要快得多

  3. 部署第三方构件:组织内部生成的私有构件需要安装在私服上供团队内使用,而且一些受版权保护的构件(比如Oracle的JDBC驱动)也不会出现在中央仓库上,这些需要安装到私服上。

  4. 增强稳定性,增强控制。

  5. 减轻对中央仓库的压力:这是从中央仓库的角度来说的,Maven鼓励建立私服,而不是让所有开发人员都直接访问中央仓库。

  1. 远程仓库的配置

从超级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

忽略校验错误。


  1. 远程仓库的认证

在settings.xml中配置仓库的认证信息,如下所示:

<settings>

……

<servers>

<server>

<id>deploymentRepo</id>

<username>repouser</username>

<password>repopwd</password>

</server>

</servers>

……

</settings>

其中的id属性必须和POM文件中配置的repository元素的id完全一致(见上一节里面配置的repository)

  1. 部署至远程仓库

往往日常开发的构件需要部署到远程仓库中,需要在项目的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会判断出当前构件时快照版本还是发布版本。

  1. 快照版本

什么是快照版本?

类似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下载同一个版本的构件。

在什么情况下使用快照版本?

快照版本只能用于组织内部。项目不应该依赖于任何组织外部的快照版本,因为快照版本是不稳定的,随时可能会更新,这样会导致项目的构建今天是成功的,而由于外部快照版本的变化,以后的构建却不一定成功。

 

  1. 从仓库中解析依赖的策略

总体的原则是这样的:

  • 如果本地仓库中没有需要的构件,则从远程仓库下载

  • 当依赖版本为SNAPSHOT时,Maven自动找到最新的快照

  • 当依赖版本为RELEASE或LATEST时,Maven会计算出最新的发布版本和最新的版本(包括快照版本)的值,然后在本地仓库或远程仓库中查找该版本的构件。不推荐使用RELEASE和LATEST,因为它们带有不确定性,构件的发布版本或最新版本发生变化时,使用者不会得到通知,所以出错时,往往不方便定位问题。

  1. 镜像

如果仓库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>

可以将私服配置为所有外部公共仓库的镜像,因为私服可以代理所有的公共仓库。

  1. 仓库搜索服务

可以在这些网站搜索Jar包:

生命周期和插件

  1. 概念

生命周期

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>


  1. 插件配置

可以通过命令行和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>


  1. 获取插件信息

从下面站点可以获取插件信息,以了解插件有哪些可配置点:

也可以使用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'



  1. 插件前缀

插件前缀和groupId:artifactId是一一对应的,这种匹配关系存储在仓库元数据中,位于groupId/mavenmetadata.xml中,这里的groupId是公共插件仓库的默认groupId,有两个取值:org.apache.maven.plugins和org.codehaus.mojo,也可以是自定义插件仓库的groupId。执行插件目标的时候,Maven会根据插件前缀找到对应的插件信息。

聚合与继承


  1. 聚合与继承

聚合与继承都是为了方便管理项目,更好的在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也可以用来管理插件。


  1. 导入依赖

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>

  1. 反应堆

反应堆是指,在多模块的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


 

    1. 用Nexus创建私服

  1. 下载安装

Nexus有免费版和专业版,免费版的下载地址是:http://www.sonatype.org/nexus/go

这是直接解压就能运行的,它自带jetty容器。还有一种war包形式发布的版本,需要放在容器里面才能运行。也在同一个页面下载。

解压后,可以按下面的方式运行:

nexus-2.3.1-01\bin\nexus.bat -start

然后在浏览器里面访问:

http://localhost:8081/nexus

管理员的默认用户名和密码分别是: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>



  1. 使用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

 

  1. 使用Hudson进行持续集成

概念

持续集成想要达到的目的:

  • 快速集成

  • 高频率自动集成

  • 自动构建:编译、测试、打包、部署、审查

  • 集成团队所有成员的最新源码

  • 集成结果及时反馈给开发人员

  • 集成结果自动部署到私服中

一个典型的持续集成场景如下图所示:


一次完整的集成往往包括以下6个步骤:

  1. 持续编译:CI服务器按一定频率检查源码控制系统,如果有新代码就触发一次集成

  2. 持续数据库集成:每次发现新的SQL脚本,就清理集成环境的数据库,重新创建表结构,并填入预备的数据。

  3. 持续测试:每次集成式运行自动化测试

  4. 持续审查:审查测试覆盖率报告,CheckStyle报告,PMD报告等

  5. 持续部署

  6. 持续反馈

持续集成带来的好处:

  • 尽早暴露问题

  • 减少重复操作

  • 简化项目发布

  • 建立团队信心:因为持续集成会覆盖大部分项目中的问题

Hudson

Hudson比较容易安装和运行。具体的配置可以参考第11章 “使用Hudson进行持续集成”。

 

 

  1. 使用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>



  1. 使用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代码之后,不需要重新构建,打包,部署,直接就能看到效果,加快了开发的速度。

  1. 使用Cargo实现自动化部署

cargo可以帮助实现在各种容器下的自动部署。

cargo的主页:http://cargo.codehaus.org/Home

  1. 版本管理

下图可以说明快照版本和发布版本之间的演变:

版本的含义,以1.3.4-beta-2为例:

1:表示该版本是第一个重大版本

3:表示这是基于重大版本的第三个次要版本

4:表示该次要版本的第4个增量

beta-2:表示该增量的某一个里程碑


<主版本>.<次版本>.<增量版本>-<里程碑版本>

  1. 灵活的构建

  2. 生成项目站点

2013年4月12日0:13:42 完

 

posted on 2013-04-28 14:42  seeker2012  阅读(604)  评论(0)    收藏  举报