Maven note
1.环境变量的设置
windows :
添加新变量 M2_HOME ,指向maven的目录
对path环境变量添加maven的安装路径(运行路径), %path%;%M2_HOME%\bin
linux:
把maven程序包压缩到自己指定的目录,
创建一个符号链接,指向maven的目录, >$ ln -s apache-maven-3.0 apache-maven
在shell中创建环境变量:
export M2_HOME=/**/**/apache-maven //这里环境变量指向符合连接,符号链接指向真正的maven目录
export PATH=$PATH:$M2_HOME/bin
可在启动shell脚本(如:~/.bashrc)中加入下面两条命令.
升级:
windows只有修改M2_HOME的新包的目录就行
linux 只要把原来的符号连接删除,在建一个符号链接指向新的maven目录
2. maven 的目录结构
bin //mvn命令的远行脚本
boot //有一个类加载器, Maven只有它加载自己的类库
conf //有个非常重要的文件settings.xml; 修改它,就是在本机上全局定制maven的行为; 一般会把它复制到~/.m2/目录下
lib //maven运行时需要的java类库,maven的各个模块库都在此, 在这还能找到maven的超级POM
**.txt //几个txt
2.1 ~/.m2 目录
该目录不在maven安装目录, 在用户家目录下
~/.m2/repository 该目录就是本地jar的仓库, 依赖的jar ,如果本机没有就去中央仓库搜索下载到这个目录下
上面提到过; 最好把 M2_HOME/conf/settings.xml 文件复制到 ~/.m2/settings.xml 这个是最佳实践,后面会详解,修改 ~/.m2/settings.xml 是属于用户级别的,如果用了新版本,那么这些配置可以延用, 不用去修改性版本中的conf/settings.xml
2.2.设置代理
当ping 中央仓库 repol.maven.org , 不通时需要挂代理了, (检查代理服务器是否同 telnet ip 端口,没报错就是同的)
编辑前面讲的settings.xml 文件,加入代理配置信息:
<settings>
<proxies> //可以多个proxy元素,默认情况下地狱个被激活生效
<proxy>
<id>my-proxy</id>
<active>true</active> //激活代理
<protocol>http</protocol> //代理协议
<host>211.22.22.11</host>
<port>3128</port>
</username>***</name> //如果代理要用户名和密码的话加上
<password>****</password>
<nonProxyHosts>www.bb.com| *.tt.cn</nonProxyHosts> //指明不需要代理的主机名可以有多个, |为分隔符, * 通配符
</proxy>
...
</porxies>
</settings>
2.3 设置 MAVEN_OPTS环境变量
maven在构建大型项目使用java默认的 可能会内存溢出; 可以不把MAVEN_OPTS的值设为-Xms128m -Xmx512m ;
这个环境变量设置和M2_HOME设置方式类似
2.4 配置用户范围settings.xml 就是~/.m2 目录下的settings.xml
2.5 ide中的自带maven版本和命令行(自己装的版本)保持一致
eclipse:
windows>Preferences>Maven>Installation>可以看到一个默认的已安装的maven,单击add按钮,然后选择Maven安装目录M2_HOME,
NetBeans:
它会默认侦测PATH环境变量,所以和命令行的Maven环境一致的,可以在 菜单栏的工具>选项>其他>Maven标签栏,看到其配置
第三章 入门
Hello World 的POM
<?xml version="1.0" encoding = "UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook</groupId>
<artifactId>hello-world</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Maven Hello World Project</name>
</project>
元素解释:
project: pom.xml文件根元素.
modelVersion: POM模型的版本.
最重要的是groupId,artifactId和version,这三个元素定义了一个项目基本的坐标,在Maven的世界里,任何jar,pom,或者war,都是基于这些基本的坐标进行区分
groupId : 定义了项目属于哪个组,往往和项目所在的组织或公司存在关联;比如在googlecode上建一个myapp, 那么groupId应该是com.googlecode.myapp
如果你公司是mycom, 有个项目为myapp, 那么groupId应该是com.mycom.myapp
artifactId: 定义了Maven项目在组中唯一的ID,我们为这个Hello World项目定义artifactId为hello-world
version: 指定Hello World项目当前的版本,SNAPSHOT 意为快照,说明项目处于开发中,是不稳定的版本,version会不断更新,如:1.0, 1.1-SNAPSHOT, ... 后面会讲管理项目版本的升级发布
name: 是对用户更友好的项目名称,虽然不是必须的,但推荐每个POM声明name,以方便信息交流
小结:
没有java代码就定义了一个Maven项目POM,但项目升级版本是只需要修改POM , 不需要修改Java代码, 在POM稳定后,日常的Java代码开发工作基本不涉及POM的修改
3.2 编写主代码
按maven的约定, 在pom的当前目录下创建目录src/main/java 接着按照groupId的定义创建子目录 com/juvenxu/mvnbook 项目有下一级目录就接着创建 helloworld/HelloWorld.java
最终在pom的当前目录下的子目录是src/main/java/com/juvenxu/mvbook/helloworld/HelloWorld.java
类HelloWorld.java 的包名是从java目录后的目录名: com.juvenxu.mvnbook.helloworld
项目主代码和测试代码不同, 主代码会被打包到最终的jar中(或war),测试代码不会打包,
此时在命令行进入pom文件的同级目录下,使用mvn clean compile 来对当前的项目编译了
clean: 清空输出目录target/, 也就是上次编译生成的.class文件
compile: 编译项目主代码,就是把编译生成的.class文件放入到target/目录下
执行该命令后,其实可以从提示信息中看到,整个执行的过程:
1.clean > deleting directory D:\**\target //删除目标文件
2.resource > //这里没有定义项目资源,先略过
3.compile > Compiling 1 source file to D:\**\target\classes //编译源代码, 到目标文件
3.3 编写测试代码
为了项目结构清晰, 主代码和测试代码是分别在独立的目录中的,
Maven项目默认的主代码目录是src/main/java
测试代码目录src/test/java
1.现在先手动创建测试目录
2.测试利用到JUnit的包, 所有要在POM的文件中加入JUnit依赖
pom文件中,在当前的<name>元素下添加依赖元素<dependencies>
...
<name>Maven Hello World Project</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
...
dependencies: 可以包含多个dependency元素,以声明项目的依赖,这里添加了一个依赖:groupId是junit, artifactId是junit, version是4.7
groupId,artifactId,version 是任何一个Maven项目的基本坐标, JUnit也不例外,有了它,Maven就能自动下载junit-4.7.jar了
使用maven,就不用自己去项目官网下载包了,它会自动访问中央仓库(http://repo1.maven.org/maven2/)下载需要的文件,可以自己上这个网站
scope: 为依赖范围,当前是test表示该依赖只对测试有效,换句话说, 在测试代码中的import JUnit代码是没有问题的,但在主代码中用import JUnit代码会造成编译错误
如果不声明依赖范围,那么默认值就是compile, 表示该依赖对主代码和测试代码都有效
现在在目录src/test/java/com/juvenxu/mvnbook/helloworld/HelloWorldTest.java 文件
这里用到了JUnit,它有一个约定,测试类中哦测试方法一test开头, 还有用@Test注释
执行测试:mvn clean test
如果本地仓库没有JUnit的包,会从网上下载
插曲:
在项目的POM中加入下面的配置可以指定用什么版本的JDK编译项目
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source> //使用1.8JDK编译代码
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
3.4 打包和运行
mvn clean package //该命令会执行编译,测试等操作,最后打成的包的名字是根据"artifactId-version.jar" 的命名;可在pom中使用finalName元素来自定义包名的名字
//输出的jar包在target/目录下; 可复制出来放其他项目中使用了
mvn clean install //该命令会把jar包和该项目的pom文件安装到本地仓库中, 这样在其他项目中以来这个jar的话,配置好依赖,就会从本地仓库里引用了,
主要命令总结:
mvn clean compile
mvn clean test
mvn clean package
mvn clean install
这些命令其实是有嵌套关系的, 执行最后一个命令install的时候其实是从第一个compile 开始执行的..
mvn archetype:generate 使用该命令可以自动创建maven项目的骨架,会要求输入groupId,artifactId,version,package;
如果要让jar是个可执行文件,也就是有main的,就需要另一个插件,maven-shade-plugin,
其实他的作用很简单就是在jar中的META-INF/MANIFEST.MF文件中加入一行 ;如: Main-Class: com.juvenxu.mvnbook.helloworld.HelloWorldTest
插件的配置代码如下
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<id>default-clean</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.juvenxu.mvnbook.helloworld.HelloWorld</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
此时可以在命令允许这个jar包: java -jar ***.jar
3.5 使用Archetype生成项目骨架
mvn archetype:generate 使用该命令可以自动创建maven项目的骨架,会要求输入groupId,artifactId,version,package;
一开始会跳出多种项目骨架,上千种,只要输入编号就行;可以不输入编号直接回车
第五章 坐标和依赖
5.2 坐标详解
<groupId>com.juvenxu.mvnbook</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
groupId : 定义maven项目属于那个项目(项目属于谁); 一般使用公司或组织域名倒置(像jar包的命名空间那样)
artifactId : 定义实际项目(模块),也就是那些jar包的文件名
version : 定义jar包的版本(maven有自己的版本规范)
packageing : 定义maven项目的打包方式, 也就是拓展名是jar,还是war; 这个没有定义的话默认是jar
classifier : 该元素不能直接定义, 他生成项目的附属构建如doc , sources 的jar
5.4 依赖的配置
在<dependencies> 元素下可配置多个子元素<dependency>通过个groupId 等来定位依赖包,详细信息见demo
详解dependency下元素的介绍
1.groupId,artifactId,version : 已经介绍过好几次了, 定位maven项目坐标
2.type: 类型 如,jar,war等, 依赖项目包的类型
3.scope: 依赖范围;有5种类型如下:
1.compile:默认,对编译,测试,运行3种classpath都有效;
2.test: 在测试阶段才有效
3.provided: 在编译和测试阶段有效,运行时无效; 如servlet-api.jar 因为应用服务器都有这个包,所以,不用运行的包如war中打包进去了
4.runtime:运行时, 如JDBC,开发的时候用的是jdk, 在部署的时用的是对于数据库的驱动包
5.system: 系统依赖,自己来指定包的存放路径,不需要maven来管理了,和provided的范围相同;那么该maven项目移植性差,
使用maven的目录就是为了依赖管理,这样maven会自动根据依赖的要求去下载包, 用这个后,差不多就是关闭了依赖
设置为该类型后,还要家一个元素<systemPath>,来指明jar包在系统的目录
6.import: 后面讲
4.option:标记依赖是否可选
5.exclusions:用来排除传递性依赖
5.6 传递性依赖
概念:
拿springframework为例子, 比如项目依赖spring, spring依赖其他项目, 那么我们只要引入spring就可以了,至于spring自己要引用那些包交给maven来管理了(帮忙下载了), maven只不过是通过spring项目的pom文件中的依赖配置,去下载的.
那么spring的依赖包,其实也是我们项目的依赖(间接的)
问题:
对于间接依赖的一些规则:
如果多个第三方包的的依赖的依赖相同了,但版本不同时,
1. 那么路径最近的依赖被解析,
2. 如果2个依赖的依赖路径相同,那么看谁先声明,解析谁
5.8 可选依赖
可选依赖的配置,如:<optional>true</optional>,在自己的项目中设置为true后,
说明: 如果项目A的依赖关系: A -> B , B -> X(可选)
B -> Y(可选)
如果不是可选的话,默认情况下,A因为依赖传递的关系,都能引用到X,Y包里的类,
现在是可以选的,那么A 是引用不到X,Y包里的东西的,所以A 要依赖与B ,那么B 又依赖与X或Y ,A项目中为了要用B必须要自己声明X或Y的依赖
原因: 如B项目是一个数据库访问工具, 依赖Mysql的驱动包, 或Oracle包, 当我们的项目是Mysql,那么我们自己要引入MySql包, B项目就能运行了.
另外说明: 理想情况不要用可选依赖, 因为使用依赖传递, 对于上面的B项目,应该拆分为2个项目,配置不同的artifactId如:B-MySql 和B-Oracle;
这样使用B项目的用户就不用关心依赖的包(不用自己再去写一个依赖关系).
5.9.1排除依赖(依赖替换)
如果当前项目A , 依赖B项目,B依赖X(SNAPSHOT版本), 那X项目会成功当前项目的依赖,而SNAPSHOT的不稳定会影响当前项目,要做的就是排除X项目的SNAPSHOT版本,
引入稳定版本.来替换依赖.
还有一种情况: 如hibernate依赖Sun JTA API由于版权原因,不在中央库里,可以使用Apache Geronimo项目来替换
例子: 排除传递性依赖配置,排除一个依赖包(B)的依赖(X,低版本的), 然后自己定义一个高版本的X依赖; 实现依赖替换
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>...
<artifactId>...
<version>...
<!--依赖配置如下 -->
<dependencies>
<dependency>
<groupId>....
<artifactId>project-B</artifactId>
<version>...
<!--排除依赖如下,可以排除多个 -->
<exclusions>
<!--排除某个依赖项目, 只要配置groupId和artifactId,不需要version -->
<exclusion>
<groupId>mvnbook</groupId><!--要排除的groupId -->
<artifactId>project-x</artifactId><!--要排除的artifactId -->
</exclusion>
</exclusions>
</dependency>
<!--上面把 project-x 排除了;下面有把他引入,并指明使用的具体版本-->
<dependency>
<groupId>mvnbook</groupId>
<artifactId>project-x</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
</project>
5.9.2 归类依赖---maven属性(类似语言中的常量定义)
在项目中用到了Springframeworkd的各个模块, 如,spring-core-2.5.6-jar;spring-beans-2.5.6.jar ....等等;
项目中依赖的spring模块想升级的话就要对每个模块的<version>进行修改,这样比较麻烦;可以像定义常量那样,只要修改一处,那么用到这个常量的地方都会用到常量的值.
maven在pom中使用常量的方式是一个<properties>元素,称为maven属性, 其中子元素时就常量的名字(所以子元素是自定义的)
例子:
<project>
....
<properties>
<!--定义一个属性(常量)-->
<springframework.version>2.5.6</springframework>
</properties>
<dependecies>
<dependency>
<groupId>org.springframework</groupId>
</artifactId>spring-core</artifactId>
<!--maven属性的使用;语法是${属性名} 来获取值-->
<version>${springframework.version}</version>
</dependency>
</dependecies>
</project>
5.9.3 优化依赖
就是使用mvn的功能分析pom中的依赖
mvn dependency:list //列出项目中的所以依赖包
mvn dependency:tree //以树的形式显示依赖包的传递关系,可看到那些因传递性的依赖包到底是那个依赖包传递进来的
mvn dependency:analyze //可看到那些依赖在pom中声明了,但缺没有使用到, 那些包使用到了缺在pom中没有声明!
//在代码中import的第三方包都要在pom中声明! 有的忘了声明缺没报错是因为依赖包通过传递依赖帮你把包引入到classpath中了
//通过analyze, 看到包在pom中声明了,缺在项目中没有使用到,不能轻易的就删除了,他只能分析那些你在代码里import的包,分析了运行时需要的包
第六章 仓库
6.2 仓库的布局
仓库就是是jar包存放的地方,他的路径是根据maven坐标来分的目录如:groupId/artifactId/version/artifactId-version.packaging
这样通过坐标都能唯一定位到我们要的包,要是声明的依赖在本地仓库没有,可以直接去仓库看,有其他版本可以
6.3 仓库的分类
分2类, 本地仓库和远程仓库,声明的依赖先从本地找,在不到去远处仓库找,都找不到就报错
远程仓库的分类:
1.中央仓库 maven自带的仓库称为中央仓库,
2.私服 在局域网内可以假设私服.这样开发的项目可以上传到私服,给其他人使用
3.其他公开远处仓库 如,java.ne Maven库,JBossMaven库
6.3.1 本地仓库
默认在用户目录下(~/.m2)目录中,又是因为用户目录磁盘不够,先自定义仓库目录的话,可在~/.m/settings.xml,设置localRepository元素的值为仓库路径如下:
<settings>
<localRepository>>D:\java\repository</localRepository>
</settings>
刚开始~/m2/settings.xml文件是没有,可以在安装目录中找到后复制过来($M2_HOME/conf/settings.xml)
要在项目中使用Maven项目,必须在本地仓库存在, 如一个自己的A项目依赖 自己的另一个自己的项目B,那么必须先把B项目安装到仓库(mvn clean install)
6.3.2 远程仓库
安装好Maven后, 不执行Maven命令,本地仓库是不存在的.执行第一条命令后才会创建本地仓库,然后根据配置和需要才远程仓库下载构件到本地仓库(maven的插件,依赖包,自己的项目,在maven中都称为构件)
6.3.3 中央仓库
maven的默认仓库, 在$M2_HOME/lib/maven-model-builder-3.0.jar 这个包中解压的花,可以看到 org/apache/maven/model/pom-4.0.0.xml 这个文件
文件中可以看到如下配置:
<repositories>
<repository>
<id>central</id>
<name>Maven Repository Switchboard </name>
<url>http://repol.maven.org</url>
<layout>default</layout>
<snapshots><!-- 快照的版本不下载-->
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
6.3.4 私服
私服好比是远程仓库的代理, 要下载构件先向私服请求,有的话直接返回,没有的私服去中央仓库下载后,在返回.
好处:
1.节省外网宽带, 局域网网内的所以机器要一个包,其实就私服向中央仓库做一次请求就够了
2.加速Maven构建, 中央仓库的下载数据慢啊,直接在局域网内的私服下载构件快啊
3.部署第三方构件, 公司那边的项目中央仓库没有啊, 部署到私服后, 所有局域网内的开发人员都能下载到了
4.提高稳定,增强控制, 外网断了对构想项目也没什么影响,一些私服软件能对仓库加入各种控制
5.降低中央仓库的负载
建立私服在第九章讲解
6.4 远程仓库的配置
在项目POM 文件中可以配置远程仓库.下面例子是配置一个JBoss Maven仓库
<project>
...
<!-- 远程仓库的配置 -->
<repositories>
<repository>
<id>jboss<id>
<name>JBoss Repository</name>
<url>http://repository.jboss.com/maven2</url>
<releases> <!-- 开启下载正式版本的包,也等于是启用这个远程仓库 -->
<enabled>true</enabled>
</releases>
<snapshots> <!-- 不下载快照版本的包-->
<enabled>false<enabled>
</snapshots>
</repository>
</repositories>
</project>
元素说明:
<repositories>: 下可以有多个<repository>子元素, 也就是可以有多个远程仓库!
<id> :id必须是唯一的,如果id的值有冲突,后面的配置会覆盖前面的配置,如果这里把id值设为central ,那么中央仓库就是当前的配置了
<url>: 仓库的地址,一般是http,可以在浏览器中浏览构件
<releases>: 发布版本;他的子元素<enabled> 值为true时才表示该远程仓库的启用,
<snapshots>: 快照版本, 和<releases>元素其实还有2个子元素,例子如下
<snapshots>
<enable>true</enable>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<updatePolicy>: 从远程仓库检查更新的频率,默认:daily :每天检查一次
nerver :从不检查更新
always :每次构件都检查更新
interval :X -- 每隔X分钟检查一次更新(X为任意整数)
<checksumPolicy>: 配置Maven检查检验和文件的策略.当构件被部署到Maven仓库是,会同时部署对应的较验和文件,
在下载构件时,Maven会验证校验和文件,如果校验失败,在该元素设置为 warn: 执行构建是输出警告信息(默认值)
fail: 让构件失败
ignore: 使maven完全忽略校验和错误
6.4.1 远程仓库的认证(帐号密码)
大部分远程仓库不需要认证的,能直接访问.但处于安全考虑,组织(公司)内部的Maven仓库服务器(私服),会有用户名和密码,
那么本地的Maven要访问该仓库内容,就需要配置认证信息.该信息不能配置在POM中,因为POM会被提交到代码仓库会被所以成员看到
认证信息是配置在settings.xml中,例子如下
<settings>
...
<servers>
<server>
<id>my-proj</id>
<username>repo-user</username>
</password>repo-pwd</password>
</server>
</servers>
</settings>
该配置信息中最重要的是<id> 元素, 它会和POM中的配置仓库id相对应.
6.4.2 部署至远程仓库
在公司那边,要把开发完的构件(项目,jar),部署(上传)到私服,需要在POM文件中加入配置元素<distributionManagement>;例子如下:
<project>
...
<distributionManagement>
<repository>
<id>proj-releases</id>
<name>便于理解的人类可读的名字</name>
<url>http://....</url>
</repository>
<snapshotRepository>
<id>proj-snapshots</id>
<name> ... </name>
<url>...<url>
</snapshotRepository>
</distributionManagement>
</projiect>
元素介绍:
<repository> : 根据项目的版本,发布版本的话会部署到这个仓库
</snapshotRepository> : 快照版本会不是到该配置的仓库
<id> : 一般要部署的私服都需要认证, 这个id和前面介绍的在settings.xml配置仓库认证信息的id对应
部署的信息配置好以后, 可以使用mvn clean deploy 命令将项目部署到私服,
6.5 快照版本
maven为什么要区分发布版和快照版,这样效率高啊
当一个项目A 依赖B项目时, 如果没有快照的机制,那么B更新后就要更换版本号,(提交几次更新几次),那么依赖与B项目的项目POM文件也要修改,
每个开发人员都要修改,可见其不合理了.
但是当B项目是快照版本(SNAPSHOT)时, 开发B项目的人员只要部署到私服好了,不用修改版本号,maven会给构件打上时间戳,而依赖B项目的开发人员
在根据自己的项目是不用去管快照是否更新了,maven回去私服比较的,如果有最新版的B项目的快照就下载下来,后构件当前项目,这样依赖B的项目一直能使用到最新的B项目.
Maven检测快照的项目的频率默认是一天一次.修改该配置是在POM中配置仓库的信息中有元素<updatePolicy>控制,上面有提到过
用户可也使用命令如:mvn clean install-U 来强制性的让mvn在构建项目时对依赖快照的项目进行更新在构建.
当B项目完成开发了,就可以把快照版本改为发布版本,如:2.1-SNAPSHOT => 2.1 表示版本已经稳定,且对应了唯一的构件(在仓库中),
而2.1-SNAPSHOT往往对应了大量的带有不同时间戳的构件.
快照版本的项目, 应该只在组织内部使用, 在项目中用了外部快照版本的项目,可能会使你的项目构建失败,外部的快照版本项目是不可控的
6.6 从仓库解析依赖的机制
在POM的依赖配置中如下: 会设置一个版本号
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
其中依赖版本的设置<version>4.7</version> ,这里是制定版本的,而可以使用3个关键字替换
1.RELEASE : 表示最新的发版本
2.LATEST : 表示最新的版本(可能是个发版本,也可能是个快照版)
3.SNAPSHOT : 表示最新的快照版
如果在项目的配置中使用了这些关键字,那么maven构件的时候会把本地仓库的版本和远程仓库的版本进行比较后, 下载一个对应的版本后构件项目.
检测版本的频率配置上面已经说过了, 可以使用参数-U强制检测
在实际项目中<version> 是不推荐使用这个写关键字的, 还是自己指定版本号比较靠谱,
这个好比你是用jdk1.5 还是jdk1.8 ,自己指定了,那么开发的环境是确定的,用这些关键字后,版本可能会波动
6.7 镜像
如果仓库X 可以提供仓库Y存储的所以内容,那么X就是Y的一个镜像.//下面的中国镜像已经不能用了
http://maven.net.cn/content/proups/public 是中央仓库 http://repo1.maven.org/maven2的中国镜像,在中国用速度更快,配置镜像如下:
<settings>
...
<mirrors>
<mirror>
<id>maven.net.cn</id>
<name>one of the central mirrors in China</name>
<url>http://maven.net.cn/content/grups/public/</url>
<mirrorof>central</mirrorof>
</mirror>
</mirrors>
</settings>
其中<mirrorOf>的值为central, 它是中央仓库的id. 其他的元素都好理解
但在公司内部的话, 可以把所以的远程仓库的镜像设置成公司的私服,也就是说,要获取构件,都从私服拿,私服就是所以远程仓库的镜像
配置如下:
<mirror>
<id>internal-repository</id>
<name>Internal Repository Manager</name>
<url>http://192.168.0.100/***</url>
<mirrorof>*</mirrorof>
</mirror>
这里的<mirrorof>使用了通配符* ,表示所以Maven远程仓库的镜像,任何对远程仓库的请求都会被转到<url>http://192.168.0.100/***</url>
<mirrorof>出来接收id,*,还有其他的一些配置方式:
1.external:* //匹配所以不再本机上的远程仓库
2.repo1,repo2 //使用逗号分隔符, 这个是镜像2个仓库(id)
3.*, !repo1 //使用感叹号来排除仓库; 这里的意思,匹配所以远程仓库,repo1除外
注意:
如果配置的镜像不稳定, maven不会跳过镜像去原仓库下载东西的!
6.8 仓库搜索服务
在网上通过关键字搜索某个项目的 groupId,artifactId,version 的信息
1.Sontype Nexus
http://repository.sonatype.org
2.jarvana
http://www.jarvana.com/jarvana
3.MVNbrowser
http://www.mvnbrowser.com
4.MVNrepository
http://mvnrepository.com
第7章 生命周期和插件
7.2.1 三套生命周期
分别为:
1.clean : 清理项目
2.default :构建项目
3.site :建立项目站点(可以看到项目的依赖)
每个生命周期都包含一些阶段(phase); 这些阶段是有顺序依赖的, 用户和Maven最直接的交互方式就是调用这些生命周期阶段.
那clean生命周期为例, 他包含的阶段有pre-clean, clean 和post-clean ;但用户调用pre-clean那么就只有pre-clean阶段得到执行.但用户调用clean会执行pre-clean,clean阶段
3套生命周期是互相独立的.
7.2.2 clean生命周期
它有3个阶段:
1.pre-clean 执行一些清理前需要完成的工作
2.clean 清理上一次构建生成的文件
3.post-clean 执行一些清理后需要完成的工作
7.2.2 default 生命周期
default是最核心的生命周期, 有很多阶段如下:
1.validate
2.initialize
3.generate-sources
4.process-sources : 处理项目主资源文件, 一般来说,是对src/main/resource目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中.
5.generate-resources
6.process-resources
7.compile : 编译项目的主源码, 一般来说,是编译src/main/java目录下的Java文件到项目输出的主classpath目录中
8.process-classes
9.generate-test-sources
10.process-test-sources
11.test-compile :编译项目的测试代码. 一般来说,是编译src/test/java目录下的Java文件至项目输出的测试classpath目录中.
12.process-test-classes
13.test 使用单元测试框架运行测试, 测试代码不会被打包或部署
14.prepare-package
15.package 接受编译好的代码,打包成可发布的格式,如JAR
16.pre-integration-test
17.integration-test
18.post-integration-test
19.verify
20.install : 将包安装到Maven本地仓库, 供本地其他Maven项目使用.
21.deploy : 将最终的包复制到远程仓库,供其他开发人员和Maven项目使用.
对于上述未加解释的阶段,可以根据名字猜测用途,还可以通过官方解释:http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
7.2.4 site生命周期
它根据POM中的信息,创建一个项目相关信息的站点(依赖什么的);生命周期如下:
1.pre-site : 执行一些在生成项目站点之前需要完成的工作
2.site : 生成项目站点文档
3.post-site : 执行一些在生产项目站点之后需要完成的工作
4.site-deploy : 将生产的项目站点发布到服务器上.
7.2.5 命令行和生命周期
我们在命令行里使用的mvn命令 其实调用的是某个生命周期的阶段;例子如下:
1.mvn clean : 调用clean生命周期的clean阶段 就是pre-clean 和clean 阶段
2.mvn test : 调用default生命周期的test阶段, 就是test阶段之前的所有阶段,之后的不会被调用
7.3 插件目标
Maven 定义了生命周期(生命周期阶段); 具体在生命周期阶段要执行的任务是有插件来完成的(也可称为插件的目标来完成)
插件有很多的功能,每个功能称为插件目标, 生命周期的某个阶段的任务是有插件目标完成的,(maven默认来一些生命周期阶段的插件目标的绑定,所有可以做到开箱即用,后续也能自定义)
7.4.1 内置绑定
例子说明:
1.clean阶段 : 绑定的插件和目标是: maven-clean-plugin:clean //插件名称为maven-clean-plugin ,冒号后的是一个插件的功能(目标)
2.site : mave-site-plugin:site
7.4.2 自定义绑定
处理内置绑定以外,用户还能够自己选择将某个插件目标绑定到生命周期的某个阶段上,一个常见的例子是创建项目的源码jar包(内置插件没有这个任务,需要用户自己配置).
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>
其实在该配置中可以不加<phase>,来指定阶段,在插件的目标中如果没有指定的话,它会使用默认的阶段(其实也是default生命周期的 verify阶段
如果在一个生命周期的阶段上绑定了多个插件目标(任务),那么会按配置的先后顺序来执行
7.5 插件配置
插件的目标,还能配置些个参数,来进一步的调整插件目标所执行的任务,以满足项目的需求; 几乎所有的Maven插件的目标都有一些可配置的参数,用户可通过命令行和POM配置的方式来设置参数
7.5.1 命令行插件配置
就是在命令行给插件传递参数;复用率java中-D参数 的方式实现的,例子如下
mvn install -Dmaven.test.skip=true //maven-surefire-plugin提供一个maven.test.skip参数,设置为true的时候会跳过执行测试
7.5.2 POM中插件全局配置
并不是所有的插件参数都适合从命令行配置, 有些参数的值从项目创建到项目发布都不会改变,或很少改变,对这种情况,在POM文件中一次性配置就显然比重复在命令行输入要方便.
全局的配置,插件的配置就是对插件目标的任务进行配置, 不管哪个生命周期阶段使用到该插件目标都会以改插件目标配置(参数)为准,来执行任务
例子和前面讲到的一个设置编译版本其实一样的
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1<version> //最新版的mavn 要加上版本信息
<configuration>
<source>1.8</source> //指定编译插件的目标参数为1.8 , 就是以1.8JDK编译代码
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
这个编译版本的问题还有一个等价的在settings.xml中配置编译的版本
<profile>
<id>jdk-1.7</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.7</jdk>
</activation>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.compilerVersion>1.7</maven.compiler.compilerVersion>
</properties>
</profile>
7.5.3 POM中插件任务配置
除了为插件配置全局的参数,可以给某个插件任务配置特定的参数,以maven-antrun-plugin插件为例,它有一个目标run,可以用来在Maven中调用Ant任务,
用户将maven-antrun-plugin:run绑定到多个生命周期阶段上,再加上不同的配置,就可以让Maven在不同的生命阶段执行不同的任务
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<!-- 对插件的任务细分-->
<executions>
<!-- 第一个任务-->
<execution>
<id>ant-validate</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>I'm bound to validate phase.</echo>
</tasks>
</configuration>
</execution>
<!-- 第二个任务-->
<execution>
<id>ant-verify</id>
<phase>verify</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>I'm bound to verify phase.</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
7.6 获取插件信息
在Maven的网站上找信息
7.6.2 使用maven-help-plugin描述插件
1.在参数plugin中输入插件的groupId,artifactId和verison , Maven会在命令行输出该插件的简易信息,如果省略verion会获取该插件最新的信息(也就可以知道最新的插件版本是多少了)
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin:2.1
2.在可以看某个生命周期阶段上绑定的插件信息,可以直接用生命周期阶段来查询
mvn help:describe -Dplugin=compier
3.可以看插件的目标的信息使用-Dgoal参数
mvn help:describe -Dplugin=compiler -Dgoal=compile
4.可以输出更信息的信息,加上参数detail
mvn help:describe -Dplugin=compiler -Dgoal=compile -Ddetail
7.7 从命令行调用插件
有些命令不需要绑定到Maven的生命周期阶段,如查看插件信息(mvn help:describe -Dplugin=compiler ), 项目依赖信息(mvn dependency:tree )
在命令行可以使用 groupId:artifactId:version:目标 来执行插件目标,但我们使用了简写,因为Maven引入了目标前缀的概念,
他通过前缀找artifactId,通过artifactId再在groupId,和version,具体的看下面的7.8
7.8 插件解析机制
7.8.1 插件仓库
项目的依赖的远程仓库和插件的远程仓库是不同的,需要单独配置(但官方的插件仓库对我们的构建项目够用了,如果插件是组织内部的话,可以配置一个内部的插件仓库)
配置代码如下:该配置信息可以写在POM或settings.xml中, 例子中是maven的中央插件仓库配置,供参考
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Plugin Repository</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default</layout>
<snapshots> <!-- 不使用快照的插件 -->
<enable>false</enable>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
7.8.2 插件的默认groupId
在POM中配置插件的时候,如果插件是Maven的官方插件(groupId为org.apache.maven.plugins),其实可以偷懒省略<groupId>元素.
但是不推荐使用,可以也省略不了多少,还会在团队合作中,让不熟悉Maven的成员感到费解
7.8.3 解析插件版本
在Maven的超级POM中对核心的插件都设定了版本的,所有我们的项目都会继承它,但是,在使用非核心的插件的话,推荐做法是指定插件的版本,
不设置版本的话,Maven会从远程仓库和本地仓库对比版本后使用最新的版本(release版),但是不同版本间可能会有差异,所有要自己指定版本.
7.8.4 解析插件前缀
在命令行中使用插件的时候,可以使用简洁的命令(插件的简写,也就是插件的前缀)
之所以可以使用简写(前缀写法), 是因为在仓库(本地仓库和远程仓库),有一个插件仓库元数据文件中.里面定义了前缀对应着插件(artifactId...)
如果使用的前缀不在这些元数据文件中, 会报错
本章小结:
本章介绍了Maven的生命周期和插件的概念; 重点介绍了Maven插件如何与生命周期绑定,以及如何配置插件行为;还讲到了仓库的元数据,Maven是如何利用元数据解析的
第八章 聚合与继承
软件设计人员往往会采用各种方式对软件划分模块,以得到更清晰的设计及更高的重用性;
Maven的聚合特性能够把项目的各个模块集合在一起构建,而Maven的继承特性则能帮助抽取各模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性
8.2 聚合
现在有2个模块account-email 和account-persist, 现在我们要构建2个项目的话,需要分别进入2个项目目录下执行mvn命令进行构建,
如果只要执行一次mvn命令就能构建2个项目不是更简单来, mvn的聚合(或称多模块)这一特性就是为该需求服务的.
为了实现该需求, 需要另外一个模块(书中该模块叫account-aggregator),然后通过该模块来构建项目所有模块,
account-aggregator本身也是一个Maven的项目,因此他有自己的POM,不过作为一个聚合项目,其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> <!-- 这里和子项目的groupId一致 ->
<artifactId>account-aggregator</artifactId>
<version>1.0.0-SNAPSHOT</version> <!--版本和模块版本一致-->
<packaging>pom</packaging> <!-- maven聚合项目的packaging与普通项目不同之处-->
<name>Account Aggregator</name> <!-- 建议加上, 因为在终端在输出log时可以很清晰的输出,子项目也一样-->
<modules> <!-- 聚合项目的子项目-->
<module>account-email</module> <!-- 各模块的位置:这里其实是相对位置,在当期聚合项目目录下有这个名字的目录,如果该目录和当前聚合目录在同一个目录下就要加上'../'-->
<module>account-persist</module>
<module>account-parent</module>
</modules>
</project>
提示:
聚合项目的目录中一般就一个POM ,然后是子项目的目录;这样就不见直观, 一看就知道当前的项目是聚合项目,其他的目录是模块项目.
在该聚合的POM中有一个<modules> 来定义模块项目,其中的子模块项目是对应的目录名称,是相对定位的,所有子项目是可以和聚合项目同级目录的.
其实子项目的目录名可以是随意的但不建议这里的子项目目录是子项目的artifactId同名的,这样看起来很清晰
所有正规的路子是: 集合项目的目录中包含模块项目的目录
8.3 继承
这一节要讲到父模块项目, 前面讲的聚合模块是为了方便构建, 父模块是为了方便配置(把重复的依赖,放入父模块的POM中声明),
子项目只要继承他就好了,不用重复声明那些依赖包了.
父模块,和聚合模块有点类似的地方在于,没有自己的src,只有一个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-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging> <!-- 和聚合模块类似的-->
<name>Account Parent</name>
</project>
有了父模块,可以让其他模块可以继承它, 基础代码如下:
<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>
<parent>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../account-parent/pom.xml</relativePath>
</parent>
<artifactId>account-email</artifactId> <!-- 没有定义groupId元素来, 都从父元素继承了-->
<name>Account Email</name>
...
</project>
提示:
1. 在<parent> 中的子元素<groupId>, <artifactId> 和 <version> 指定了父模块的坐标,是必须的; <relativePath>定位了父模块POM的相对路径;
但构建项目时,如果<relativePath>找不到POM, 会从本地仓库查找,其实它有默认值是../pom.xml
2. 正确设置<relativePath>非常重要, 但开发中有人只去源码库中迁出一个子项目,没有没迁出这个父项目, 那么构建会失败,因为刚开始本地仓库中也没有这个父POM,设置正确也不用去本地仓库找
子项目中还能直接继承父POM中的groupId和version; 当然子项目可以自己声明,不用继承也可以
3. 父项目也需要放入到聚合项目中;
8.3.2 可继承的POM元素
前面讲到groupId和version 可被继承, 这一节深入介绍具体有那些可以继承
1. groupId : 项目组ID , 项目坐标的核心元素
2. version : 项目版本 , 项目坐标的核心元素
3. description : 项目的描述信息
4. organization : 项目的组织信息
5. inceptionYear : 项目的创始年份
6. url : 项目的URL地址
7. developers : 项目的开发者信息
8. contributors : 项目贡献者信息
9. distributionManagement : 项目的部署配置
10. issueManagenment : 项目的缺陷跟踪系统信息
11. ciManagement : 项目的持续集成系统信息
12. scm : 项目的版本控制系统信息
13. mailingLists : 项目的邮件列表信息
* 14. properties : 自定义的Maven属性; 使用表达式${属性名} 来使用
* 15. dependencies : 项目的依赖配置
* 16. dependencyManagement: 项目的依赖管理配置
* 17. repositories : 项目的仓库配置
* 18. build : 包括项目的源码目录, 输出目录, 插件, 插件管理等等配置.
19. reporting : 包括项目的报告输出目录配置, 报告插件配置等
8.3.3 依赖管理
<dependencies>元素可被子项目继承, 可是有时我们的某个子项目不一定需要父POM中的所有依赖,只要一部分,那么使用这个元素就做不到了.
父POM使用<dependencyManagement>元素可以让子项目继承到父POM的依赖配置, 有可以灵活自定义选择哪些不需要.
下面父POM配置<dependencyManagement>元素代码
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<space>test</space>
</dependency>
...
</dependencies>
</dependencyManagement>
...
提示:
使用<dependencyManagement> 并不给父POM的项目真正的引入依赖,也不会给所有子类引入依赖, 可以看做是一种声明;子类中还要配置下才算引入依赖
下面是子类中依赖的配置信息
...
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
...
</dependencies>
...
这段配置其实只给子项目节省了<version>和<scope>2个配置,虽然省不了多少,但还是推荐使用,好处就是在父POM中对依赖版本做到了控制,
避免了子模块使用依赖版本不一致的情况, 降低依赖的冲突几率.
提示:
如果子项目不声明依赖<dependencies>,那么父POM的依赖声明其实是不产生实际效果的.
还有如果,父项目中没有声明的依赖, 子项目可以自己加.
(简单说: 父POM的是依赖声明,并不实际给子项目添加依赖, 子项目需要什么依赖要自己声明,父POM声明了的依赖可以在子项目中节省<version>和<scope>)
import依赖范围
在5.5节讲到过依赖范围,但没讲import的左右, 该范围只能在<dependencyManagement>元素下才有效果, 它的作用就是把另个POM(父POM)中的<dependencyManagement>导入到当前的POM(子项目)中.
所以使用继承或import方式把<dependencyManagement> 引入子项目都可以做到依赖版本的控制; 使用代码如下:
<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>
8.3.4 插件管理
<pluginManagement>和<dependencyManagement>的思路是一样的, 在父POM提供声明式的配置, 子项目继承了该POM要在<plugin>中定义了该插件的<groupId>和<artifactId>后才算把父POM的声明的
信息正式的引入到子项目中,如果父POM对插件的配置不满意, 子项目只要自己配置即可. 该方式的使用就算子项目都自定义了插件的行为, 但至少可以统一了插件的版本,避免了一些不必要的麻烦
例子:父POM
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
子项目;
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <!-- 子项目只要有父POM中的插件声明的定位信息即可, 如果不满意父POM的配置,可以继续自定义-->
</plugin>
</plugins>
</build>
8.4 聚合与继承的关系
聚合模块: 方便构建所以模块, 它知道有多少个需要构建的项目, 但真正的项目并不知道有他的存在
继承模块: 消除重复的配置,方便管理配置信息. 它不知道有多少个子项目继承了它, 但真正的项目(子项目)都知道它.
这给个2个模块就只有各自的POM文件, 所以可以是合并为一个模块.实际开发也就一个模块即有聚合又有继承的功能
只要把继承POM中的些继承元素复制到集合模块的POM就行了, 因为,聚合模块就一个<modules>元素及其子元素,父POM是没有,前提都是兼容,
但是子模块本来和父POM是一级目录并使用到了<relativePath>来指定父POM的路径, 现在因为聚合模块可以冲当父POM了,默认不需要<relativePath>元素了, maven会去上级目录找这个父POM
8.5 约定优于配置
对maven项目, maven 的用户都知道源码在哪,输出的包在哪, 使用什么命令来操作maven项目等.因为这些都是约定好的.
其实源码的位置,输出的包的位置什么的都是可以自定义的, 但不建议那么做,因为你这么做了还要解释给其他人听为什么那么做,要不然别人不能和你一起开发了,增加了沟通成本!
但有的老项目,为了使用maven没办法的情况下,就可以是来设置源码的路径啊什么的.
这些约定的配置, 其实都是在超级POM中定义的. 因为我们的maven都是继承自它, 类时java中的对象都继承自Object;
8.6 反应堆
就是maven在多模块构建时, 会根据POM,来选择构建顺序.
聚合模块, 先看自己有没有父模块或依赖, 没有就先构建自己, 然后开始构建子项目, 子项目先看他有没有父模块或依赖模块, 有就构建,完了,否则就死循环了构建8.6.2 裁剪反应堆该子项目, 就这构建下去
所以在maven中子模块之间是不允许互相依赖的,否则就死循环了
8.6.2 裁剪反应堆
就是再利用聚合特性构建多个模块的基础之上的拓展.
有使用我们并不一定要构建所以的子模块, 那么应用下面的 命令参数可以灵活的构建项目
1. mvn clean install -pl account-email, account- persist ; // 使用 -pl 选项指定构建某几个模块
2. mvn clean install -pl account-email -am ; //构建指定的模块, 并构建他的依赖模块
3. mvn clean install -pl account-parent -amd ; //构建指定模块的子模块
4. mvn clean install -rf account-email ; //构建反应堆中的所以模块,特别指定对应模块先开始
5. mvn clean install -pl account-parent -amd -rf account-email; //-rf选项可以在其他选项的基础上使用
第九章 使用Nexus创建私服
私服,可以减少中央仓库依赖, 加速构建, 可以部署组织那边的项目模块.
市面上有3种私服: apache Archiva ; JFrog Artifactory ; Sonatype Nexus ; Nexus最流行
9.1 Nexus 简介
1. 内存占用小; 2. 基于ExtJS 的web界面; 3. 基于Restlet的REST API; 4. 支持代理仓库,宿主仓库和仓库组
5. 不需要数据库; 6. 支持仓库索引和搜索; 7. 支持从浏览器上传Maven构件; 8.细粒度的安全控制
专业版是收费的,对了写企业级的管控
9.2 Nexus安装
有2个版本, 一个是war包, 一个是 Bundle包(直接使用, 内涵web容器和war包)
下载:
http://nexus.sonatype.org/downloads
Bundle包的使用:
将下载的包解压后会有2个子目录
nexus-webapp/ : 该目录是应用本身
sonatype-work/ : 该目录是用户使用的一些配置,如配置文件,日志, 仓库文件等, 要备份和移植之需要拷贝该文件即可
应用使用脚本介绍:
windows的使用: 脚本的目录nexus-webapp/bin/jsw/windows/
nexus.bat : 启动应用
Installnexus.bat : 安装成windows服务
Uninstallnexus.bat : 卸载服务
Startnexus.bat : 启动Nexus Windows服务
Stopnexus.bat : 停止Nexus Windows服务
pausenexus.bat : 暂停Nexus Windows服务
Resumenexus.bat : 恢复暂停的Nexus Windows服务
借助Windos服务可以,让Nexus伴随Windows自动启动
Linux的使用: 脚本在 nexus-webapp/bin/jsw/linux/
./nexus console : 启动应用
./nexus start : 启动应用 (后台运行)
./nexus stop : 停止应用(在后台运行着的应用)
./nexus status : 查看应用的状态
./nexus restart : 重启
提示:
在使用中主要的问题会是端口冲突, 默认是8081; 需要修改在nexus-webapp/conf/plexus.properties 中的application-port属性修改值后重启Nexus应用既可
war 包的花和其他web应用一样的用法
应用启动后可以在浏览器中访问了 http://localhost:端口/nexus/
9.2.4 登录 Nexus
它有严重的权限控制, 默认管理员帐号是admin密码admin123
9.3 Nexus 的仓库与仓库组
仓库是核心
9.3.1 内置仓库
界面导航栏中的Repositories链接,之后就能看到仓库列表
有4中类型: group(仓库组) ; hosted(宿主) ; proxy(代理) 和 virtual (虚拟) ;仓库的格式为maven2或maven1
仓库还有一个属性为Policy(策略); 表示该仓库为发布(release)版本仓库 还是快照(snapshot)版本仓库; 最后还有仓库的状态和路径
仓库用途:
虚拟仓库: 动态将仓库内容格式转化,为maven1格式服务, 可以忽略
Maven Central : 代理Maven中央仓库, 策略为release, 因此只会下载和缓存中央仓库中发布版本的构件
Releases : 宿主仓库,策略为release, 用于部署组织内部的发布版本构件
Snapshots : 宿主仓库, 策略为snapshot, 用于部署组织内部的快照版本构件
3rd party : 宿主仓库, 策略为release, 用来部署无法从Maven中央仓库获取的第三方发布版本构件(如jdbc驱动)
Apache Snapshots : 代理仓库, 策略为snapshot, 用来带来Apache Maven仓库的快照版本
Codehaus Snapshots : 代理仓库, 策略为release, 用来带来Codehaus Code Maven仓库的发布版本
java.net-Maven2 : 代理仓库, 策略为release, 用来带来java.net Maven仓库的发布版本
Public Repositories : 该仓库将上述所有策略为release的仓库集合并通过一致的地址提供服务.
Public Snapshot Repositories : 该仓库将上述所有策略为snapshot的仓库集合并通过一致的地址提供服务.
各种细分的仓库是为仓库管理员,方便管理设置, 最后来个Public 仓库是为了开发人员方便获取(部署)构件而设立的.
9.3.3 创建Nexus宿主仓库
就是服务器的本地仓库, 可为一个项目创建一个宿主仓库,便于管理(使用)项目的构件.也能便于项目管理, 比如只有该项目的开发人员才有权限访问该项目仓库
9.3.4 创建Nexus代理仓库
如对maven的中央仓库进行代理, 这也每次问需要开源构件时都,可以缓存在这个代理仓库中, 项目中的开发人员只要就不用每个人都去访问Maven的中央仓库了, 内网的速度下载速度肯定是最快的
9.3.4 仓建Nexus仓库组
仓库组是一个抽象层, 当把多个不同类型的仓库放入一个组中, 那么我们的项目中要下载构件时只要问"仓库组"要就可以了,仓库组会在各具体的仓库中搜索构件. 这样对开发人员更方便
9.4 Nexus的搜索与构件搜索
Nexus可以代理Maven的中央仓库后配置下载中央仓库索引后, 就可以在私服上搜索中央仓库中的构件了, 但是网上有网站提供免费搜索Maven中央仓库的服务,自己没有必要去搞这个索引,因为索引非常大,还要每天更新
搜索构件的技巧:
1. 可以根据groupId 或ArtifactId和version信息来搜索定位一个构件
2. 如果知道某个Java类会在构件中使用, 可以根据这个类带定位构件
3. 可以拿 校验和的值 来搜索一个构件
9.5 配置Maven 从私服下载构件
1. 在项目POM中配置 私服仓库之对当前项目有效; 配置如下:
<project>
...
<repositories>
<repository>
<id>nexus</id>
<name>Nexus</name>
<url>http://localhost:8081/nexus/content/groups/public/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus</id>
<name>Nexus</name>
<url>http://localhost:8081/nexus/content/groups/public/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories>
...
</project>
2. 给本机设置私服仓库, 这样所以项目构件时都会用私服的仓库, 需要在settings.xml中配置,和POM中配置有点不同
<settings>
...
<profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>nexus</id>
<name>Nexus</name>
<url>http://localhost:8081/nexus/content/groups/public/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
和上面类似
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles> <!-- 这个不能少-->
<activeProfile>nexus</activeProfile> <!--profile元素的id元素值 -->
</activeProfiles>
...
</settings>
3. 对所以构件的获取都从私服上下载(就是对中央仓库配置一个镜像)
<settings>
...
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf> <!-- 通配符* 表示对所以的仓库都使用这个镜像-->
<url>http://localhost:8081/nexus/content/groups/public/</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>central</id> <!-- 覆盖id为 central仓库的配置-->
<name>Nexus</name>
<url>http://无所了所仓库都配置了镜像,所以会访问镜像的url</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
//插件仓库配置类时,之是父元素不同参考上面的
</profile>
</profiles>
</settings>
9.6 部署构建至Nexus
如果代理中央仓库做缓存,那么上面的配置已经够用, 现在要讲下,组织内部的构件,部署在宿主仓库供内部下载,还有一种有版权的第三方构件, 因为中央仓库没有,所有要需要在宿主仓库中;
可以同Maven自动部署到宿主仓库, 也可以在界面上手动上传构件
9.6.1 使用Maven部署构件到Nexus
在项目的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>
光给项目配置部署服务器是不够的, 因为服务器是有权限的,需要在settings.xml中配置对应的账号密码, 配置如下:
<settings>
...
<servers>
<server>
<id>nexus-releases</id> <!-- 对应项目中要部署的仓库id-->
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>nexus-snapshots</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>
</settings>
9.6.2 手动部署第三方构件至Nexus
有的构件如Oracle的JDBC驱动,有版权,在中央仓库中没有,或者一些小型开源项目, 在中央仓库中没有, 那么就要手动上传他们在我们的服务器上了.
选择一个宿主仓库(如3rd party),选择Artifact Upload , 上传够贱需要填写Maven坐标,是Maven项目的话,就直接可以在GAV Definition选择From POM,
允许上传一个主构件和多个附属构件.
9.7 Nexus的权限管理
管理员可以自由地根据需要配置Nexus用户,角色,权限等.
9.7.1 Nexus的访问控制模型
管理员必须以角色的方式给Nexus的用户赋予权限, 一个角色可以有多个权限, 一个人可以有多个角色, 角色还可以嵌套在另一个角色中.
Nexus预定义了三个用户:
admin : 对Nexus服务完全控制,默认密码admin123
deployment : 该账户对仓库有很高的权限, 但对Nexus服务器没有配置权限, 默认密码deployment123
anonymous : 为登入的账号, 可以浏览仓库和搜索
在Users 页面可以添加用户
预定义的几个重要角色:
UI: Basic UI Privileges: 包含访问Nexus界面必须的最基本的权限
UI: Repository Browser: 浏览仓库页面所有需要的权限
UI: Search: 访问快速搜索栏及搜索页面所需要的权限
Repo: All Repositories(Read): 给予用户读取所有仓库内容的权限,没有该权限就不能下载
Repo: All Repositories(Full Control): 给予用户操作仓库的所有权限, 增删改查
还有其他的一些,对管理员有用
9.7.2 为项目分配独立的仓库
在组织内部,如果项目不管是快照还是发布办,或其他项目都在一个仓库, 那么可能会有冲突, 还有安全的问题(其他项目的人可以操作别人的构件).
给项目分配独立的仓库, 把仓库的部署,修改,删除权限给对应项目成员,其他用户只读,下载,搜索内容.
具体操作细节用到再说吧
9.8 Nexus的调度任务
有很多预定义的, 如删除回收站,删除代理仓库长期未被使用的构件缓存,下载远程仓库索引...
9.9 其他私服软件
Apache的Archiva 和 JFrog的 Artifactory
第十章 使用Maven进行测试
随着敏捷开发模式的流行, 单元测试变的尤其重要, Maven的重要职责之一,自动运行单元测试, 它通过maven-surefire-plugin插件与主流的单元测试框架JUnit3,JUnit4以及TestNG集成,
并且能够生成丰富的结果报告.
10.1 account-captcha
再加一个项目来讲解本章的内容. 该模块负责处理账号注册时生成验证码key,和图片, 以及校验等.
10.1.1 account-captcha的POM
<project ... >
<modelVersion>4.0.0</modelVersion>
<!-- 定义父POM-->
<parent>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<!-- 项目的坐标,因为继承了父POM的groupId,所以下面省略了-->
<artifactId>account-captcha</artifactId>
<name>Account Captcha</name>
<proerties> <!-- 常量-->
<kaptcha.version>2.3</kaptcha.version>
</properties>
<!-- 定义依赖-->
<dependencies>
<dependency>
<groupId>com.google.code.kaptcha</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
<classifer>jdk15</classifer> <!-- 之前没用过的属性,定义是jdk1.5的jar包, 因为google提供了相同版本不同jdk的实现jar包-->
</dependency>
<dependency>
<groupId>org.springframework</groupId> <!-- 父POM中声明的依赖, 在项目引入该依赖-->
<artifactId>spring-core</artifactId>
</dependency>
</dependencies>
<!-- 声明一个公共仓库; 因为google的那个包Maven中央仓库中没有, 如果自己私服中有就不需要声明这个仓库了-->
<repositories>
<repository>
<id>sonatype-forge</id>
<name>Sonatype Forge</name>
<url>http://repository.sonatype.org/content/groups/forge/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
</project>
项目的POM写好后, 记得要在聚合POM中把项目加入<modules>元素中
10.1.13 account-captch 测试代码
测试日志输出中有几个字段:
1.run : 运行了多少个测试方法
2.Failures : 表示测试于结果不托付
3.Errors: 代码报错
4.Skipped: 跳过多少个测试方法, 如在JUnit使用@Ignore 就可以跳过测试方法
10.2 maven-surefire-plugn 简介
该Maven插件称为测试远行器, 它的作用就是在特定生命周期阶段的时候执行JUnit或TestNG的测试用例.
在默认生命周期阶段的test阶段与maven-surefire-plugin的test目标相绑定, 这是内置的绑定.
该插件的test目标会自动执行测试源码路径(默认src/test/java)下面所有符合命名模式的测试类,模式如下:
1. **/Test*.java: 命名以Test开头的Java类
2. **/*Test.java: 命名以Test结尾的Java类
3. **/*TestCase.java: 命名以TestCase结尾的Java类
符合这些约定模式的测试类,会自动执行, 要不然不会执行.当然用户还是可以自定义的, 使用的是TestNG测试集合xml文件.在10.7详解
10.3 跳过测试
在命理使用参数skipTests就可以跳过测试(要知道的是它还是会编译测试代码的,只是不运行), 在命令行使用如下:
mvn package -DskipTests //前面也讲到过跳过测试, 使用的参数是:-Dmaven.test.skip=true //它不但跳过测试对测试代码不进行编译
当然还有一种不推荐的用法是在项目的POM中对maven-surefire-plugin插件进行属性设置; 配置如下:
<plugin>
<groupId>org.apache.maven.plugin</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
对应跳过编译测试代码和跳过测试的命令参数maven.test.skip=true ,在POM中也是可以设置的(设置2个插件),如下:
<plugin> <!-- 跳过编译-->
<groupId>org.apache.maven.plugin</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin> <!-- 跳过测试-->
<groupId>org.apache.maven.plugin</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
10.4 动态指定要运行的测试用例
使用的参数还是test ,可以自定义的只测试某一个类., 例子如下:
mvn test -Dtest=FooTest //只测试FooTest.java
mvn test -Dtest=Foo*Test //*号通配符
mvn test -Dtest=Foo1Test , Foo2Test //测试2个类,逗号分
mvn test -Dtest=Foo*Test , Foo2Test //逗号和通配符的结合使用
注意:
当 mvn test -Dtest 后没有指定要测试的类,或匹配不到测试类,会报错
想让他不报错可以在加一个参数: mvn test -Dtest -DfailIfNoTests=false // 有了-DfailIfNoTests=false参数就不报错了, 这也是一种间接的跳过测试的方式
10.5 包含与排除测试用例
使用约定的测试规则当然好, 有时候只想测试部分测试用例, 有时候是旧项目在其他的文件目录下, 那么就需要自己在项目POM中进行配置了
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/*fooTest.java</exclude>
</excludes>
</configuration>
</plugin>
解释:
<includes>: 元素配置需要测试的类, 类名的命名规则可以自己定了,如下
**/*Tests.java : 匹配所有以Tests结尾的java类; 其中**用来匹配任意路径, *用来匹配0或多个字符
<excludes>: 它用来排除不需要的
10.6.1 基本的测试报告
maven生成2中格式错误报告, 文本格式, 和与JUnit兼容的XML格式(已经成为事实标准,可以被其他软件识别)
10.6.2 测试覆盖率报告
使用到的工具是Cobertura,Maven通过cobertura-maven-plugin插件集成了这个工具, 使用起来很简单,如下:
mvn cobertura:cobertura //该命令会编译代码,编译测试代码, 运行测试代码,
//然后在target/site/cobertura/目录下生成index.html的测试报告
生成的报告会显示测试的覆盖率, 哪方法测试到了,那些没有测试到,一目了然
10.7 运行TestNG测试
使用该测试框架替换JUnit, 从POM开始:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testing</artifactId>
<version>5.9</version>
<classifier>jdk15</classifier>
<configuration>
<suiteXmlFiles> <!-- 配置TestNG特有的测试集合的配置文件-->
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
<!-- 也可以配置测试组-->
<groups>util,medium</groups> <!-- 只允许这2个组中的测试方法-->
</configuration>
</dependency>
对测试类的修改,很简单只要把导入的包名该下就好, 具体的API都一样
org.junit.Test 改成 org.testng.annotations.Test
TestNG特有的功能
在项目根目录下可以有一个testng.xml,来配置测试集合, 就是测试哪几个类
<?xml version="1.0" encoding="UTF-8"?>
<suite name="Suitel" verbose="1">
<test name="Regressionl">
<classes>
<class name="com.....RandomGeneratorTest"
</classes>
</test>
</suite>
TestNG 还有一个功能, 就是把测试的方法,放入一个测试组里,然后可以配置成只远行测试组里的测试方法
@Test(groups={"util","medium"})
foo(){
...
}
10.8 重用测试代码
就是打包测试代码, 可以让其他项目继承使用,
第十一章 使用Hudson进行持续集成
在服务器上设置一定频率的自动化的,签出最新代码, 测试项目, 构建项目, 部署项目,(还能夹杂对源码的style检查)等任务,
方向问题以邮件的方式反馈给相关开发人员.
11. 持续集成的作用,过程和优势
1.快速: 开发人员本地测试后, 提交了后,希望能在测试服务器上得到及时的反馈
2.高频率: 可每隔一段时间,做一次持续集成的操作,
3.自动: 自动触发并执行持续集成的操作, 不应该手动参与.
4.构建: 包括编译,测试,审查,打包,部署等工作
5.所有源码: 最新提交的源码
6.反馈: 快捷的告诉团队成员最新的集成状态, 但集成失败的时候, 反馈报告应该尽可能地反馈失败的具体细节
持续集成的具体步骤:
1.持续编译: 所有正式的源码都应提交到源码版本控制系统中, 持续集成服务器按一定频率检查源码库,如果有新提交代码, 就触发一次集成.清除旧的编译代码.
2.持续数据库集成: 在很多项目中,源码不一定是Java, 还有可能是SQL脚本, 单独管理容易出错,造成混乱;
持续集成也应该包括数据库的集成,每次发现新的sql脚本,就应该清理集成环境的数据库,重新创建表结构,并填入预备的数据, 这样就可以随时发现脚本的错误
3.持续测试: 就是自动运行单元测试的代码,好的单元测试应该是不依赖于环境,可重复执行,并且能够自我检查的,当然还有一些是依赖外部环境的.
这些测试都要在每次集成的时候运行.并且发生问题的时候能产生具体的报告
4.持续审查: 使用Checkstyle和PMD子类的工具能帮我们发现代码中的不规范的代码风格, 持续集成可以用这些工具生成报告反馈, 当然代码风格审查频率可以是一天一次
5.持续部署: 有些问题需要部署后才能发现, 可能是容器或环境相关的, 自动部署就能帮我们尽快发现这类问题
6.持续反馈: 通过邮件, 把报告发生给相关人员
好处:
1.尽早暴露问题, 问题暴露的越早,去修复的成本就越低
2.减少重复操作,手动去走一边枯燥的流程显然很慢
3.简化项目发布,发布也是一个枯燥重复的过程
4.建立团队信心,持续集成的项目,因为一直在反馈持续集成的状态给团队成员. 让成员对项目的情况了然于心
11.2 Hudson 简介
它是一个开源的流行的优秀易用的, 当然还有其他的类似工具如开源的CruiseControl, 商业的Bamboo和TeamCity等
官网:http://hudson-ci.org
下载:hudson.war文件
启动:java -jar hudson.war
访问:http://localhost:8080
不使用默认端口方式启动: java -jar hudson.war --httpPort=8082
可以放入web服务器中使用:
如在Tomcat中使用访问: http://localhost:8080/hudson
现在Hudson不流行了, 有一个他的继承者Jenkins ,风格差不多,网站如下
Jenkins: http://mirrors.jenkins-ci.org/
启动和停止:在安装目录里 jenkins.exe start 和 jenkins.exe stop ; //linux 中是:启动命令:./jenkins.sh start 停止命令:./jenkins.sh stop
11.4 准备Subversion仓库
查看是否安装了Subversion
svnserve --version
windosw 安装 Slik Subversion(http://www.sliksvn.com/en/download)
安装的时候需要安装 complete,默认安装不带svnserve的
创建svn仓库
svnadmin create 目录名 \account //linux下建仓库:svnadmin create 目录名
创建完仓库会有几个目录文件, 可对仓库进行配置账号等...
把项目导入svn仓库
svn import -m "注释" .[项目目录] file://仓库目录
启动svnserve服务
svnserve -d -r 仓库目录 --listen -host 0.0.0.0
参数解释:
-d: svn服务器作为守护进程运行,
-r: svn仓库位置
--listen-lost: 把仓库服务器绑定在IPv4上(有的操作系统会帮IPv6上,那么hudson无法识别)
检查svn服务器是否可以
svn list svn://192.168.1.101/项目目录
11.5 基本系统设置
需要设置JDK的安装位置,Maven安装位置, 如果是Ant或Shell来持续集成项目就要设置它们,这里不用
在系统管理>系统设置>
可以配对个JDK,结合不通项目使用
11.6 创建持续集成任务
首页的新建任务, 会让你选择什么类型, 一般选Buid a free-style software project 自由风格也就是,可以自定义配置
进入下一个页面后可以看到很配置要设置,如下:
1.Discard Old Builds : 配置如何抛弃旧的构建文件,旧的构建文件不抛弃,磁盘很快要满的,有2个选项:
1)Days to keep build: 保留N天内的构建文件
2)Max # of builds to keep: 保留#个最近构建的相关文件
2.配置源码仓库: 在source code management选择subversion,也仓库地址,账号密码配置
3.构建触发配置:有3种
1)Build after other projects are built: 在其他项目构建完成之后构建本项目(不常用)
2)Build periodically: 周期性的构建本项目.(不常用, 会造成没必要的构建)
3)Poll SCM: 周期性地轮询源码仓库, 发现有更新时构建本项目(常用,可避免没必要的构建)
轮询的频率使用了UNIX任务调度工具Cron的配置语法,配置有5个字段,表示不同的时间单位,字段之间用空格分隔
每个字段意义及值范围分别如下:
* * * * *
1. 分: 一小时中的分钟, 取值:0~59
2. 时: 一天中的小时, 取值:0~23
3. 日: 一个月中的日期, 取值:1~31
4. 月: 月份, 取值:1~12
5. 星期几: 一周中的星期几, 取值:0~7 ,比较特别的是0和7都代表星期天
取值不单有数字还有一些特殊字符:
1. *: 表示匹配范围内所有值
2. M-N: 连字符, 匹配M~N 之间的范围所有值, 如1到5表示为 1-5
3. A,B,...,Z: 逗号, 匹配对个值 如:" 0, 15"; 0和15都匹配
4. */X 或 M-N/X : 范围加斜杠表示匹配范围内被X整除的值:
如:第一个字段(分钟); */5 此时的*表示0~59的范围,能被5整除的是5,10... 也就是在5分钟时,10分钟时...
在举个例子: 0~10/3 表示能被3整除的值, 第一个字段的话,就是在3分钟时, 6分钟时, 9分钟时
一般的用法是每个10分钟轮询源码库 */10 * * * *
可以使用# 添加一个注释
4. 任务的构建配置; 告诉持续集成 使用运行Maven命令构建项目. 在Build中, 选择 Invoke top-level Maven targets
然后输maven 的构建命令,如 maven clean deploy ; 每次构建都执行这个命令来构建maven命令
11.7 监视持续集成工具的任务状态
11.7.1 全局任务状态
默认主页面就是当前服务器上所有集成任务的状态
主要介绍下, 那些7个显示列的含义
s: 状态
蓝色: 任务最近一次的构建是成功的
红色:失败
黄色: 构建成功,但不稳定(有测试失败)
灰色:任务从未被执行过或被禁用
w: 天气;使用一组天气图标表示任务长期的一个状态
万里晴空, 80%以上的集成都成功
稍有乌云, 60~80%成功
乌云密布, 40~60%
阴雨绵绵, 20~40%
电闪雷鸣, 任务集成成功率不到20%
...
最后一列: 表示例子执行一次
11.7.4 Maven项目测试报告
Maven项目构建完会有一个测试报告,(文本型,和xml型),
可配置一个集成任务, 在配置页面 Post-build Actions 部分(构建后操作),选择 Publish JUnit test result report选项
在Test report XMLs 输入: **/target/surefire-reports/TEST-*.xml ;该表达式表示匹配任意该目录下的TEST-开头的XML文件.
如果用户配置了测试报告, 但在构建命令中忽略测试,或获利失败的测试, 那么不会导致构建失败, 状态也不会是红色, 检查测试失败, 那么会是黄色的状态
11.8 用户管理
持续集成在轮询代码仓库的时候会缓存提交代码的用户, 然后给这么用户配置Email 就可以发Email了
补充一些svn的知识
svn仓库是匿名可读,认证用户可写, 现在要做的是关闭匿名可读权限, 添加用户
在svn仓库下 conf/svnserce.conf文件进行编辑配置
anon-access=none #匿名用户没有任何权限
auth-access=write #认证用户有读写权限
password-db=passwd #存储用户信息的数据位于同级目录下的passwd文件中,
编辑conf/passwd文件
[users] #添加3个用户含密码
admin=admin123
juven=juven123
jason=jason123
提交代码
svn commit -m "注释" --username juven --password juven123
但持续集成工具轮询svn仓库的时候, 会把提交者的信息保存起来, 这样就可以在"用户"界面到这样开发者了,然后就是给他们配置一些信息,
最主要的信息就是Email 这样就可以把集成信息反馈给开发者了.
11.9 邮件反馈
邮件反馈的配置在系统设置页面, 找到E-mail Notification部分, 然后输入下面信息
SMTP server : SMTP邮件服务器地址
Default user e-mail suffix: 默认用户邮件后缀; 对有的代码提交这没有设置邮件地址,那么会把代码提交者的名字拼上这个后缀来发邮件
System Admin E-mail Address: 系统管理员邮件地址
Hudson URL: Hudson服务器地址, 该地址会包含在邮件信息中, 方便用户访问Hudson
SMTP Authentication: SMTP相关的认证配置; 要个邮箱帐号
配置好系统的邮件信息后, 就可以在任务的配置中使用邮件反馈了, 在Post-build Actions (构建后操作)中把E-mail Notification选上
然后就是选择什么时候发邮件
11.10 Hudson工作目录
对Hudson的各种配置, 任务, 报告以文件形式存储在磁盘的,就是Hudsond的工作目录,默认在用户目录的.hudson/目录下.
可以设置一个环境变量HUDSON_HOME=d:\hudsonwork ;来自定义工作目录
目录中文件信息详见239
第十二章 使用Maven构建web应用
这一章增加2个模块分别是: account-service 用来封装前面讲到的3个模块; account-web包含一些涉及web的相关内容,如serclet,jsp等
12.2.1 account-service的POM
<project xmls....>
<modelVersion>4.0.0</modelVersion>
<parent>
//继承父POM
</parent>
<artifactId>account-service<artifactId>
<name>Account Service</name>
<dependencies>
//依赖
</dependency>
<build>
<testResources> <!-- 指定测试时资源的路径-->
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
</build>
</project>
12.3 account-web
因为account-service 已经封装了所有下层细节, account-web只需要在此基础上提供一些Web页面, 并使用简单Servlet与后台实现交互控制
12.3.1 account-web 的POM
出了打包方式war之外, 其他都类似
<project xsi:schemaLocation="htt ......>
<modelVersion>4.0.0</modelVersion>
<parent>
//继承父POM
</parent>
<artifactId>account-web</artifactId>
<package>war</package>
<name>Account Web</name>
<dependecies>
<!-- 依赖 -->
...
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope> <!-- 该设置表示只在开发的时候使用, 打包不用,因为web服务器自带,打进包中有可能有冲突-->
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope> <!-- 该设置表示只在开发的时候使用, 打包不用,因为web服务器自带,打进包中有可能有冲突-->
</dependency>
</dependecies>
</project>
在web项目的POM中可以有一个finalName元素, 打包的时候就以这个名字来给包命名,不会带上版本号了,因为超级POM中设置了fianlName的名字带版本号
<build>
<finalName>account</finalName>
<build>
account-web的 web.xml
<web-app>
<display-name>Sample Maven Project:Account Service</display-name>
<listener> <!-- 用来在web项目启动的时候加载Spring配置文件(IoC容器)-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name> <!-- 用来指定Spring配置文件的位置,这些文件本身在其模块的根目录下, 做为依赖引入后,也就是在类路径根目录下了-->
<param-value>
classpath:/account-persist.xml
classpath:/account-captcha.xml
classpath:/account-email.xml
classpath:/account-service.xml
</param-value>
</context-param>
<servlet>
<servlet-name>
<servlet-calss>
</servlet>
</servlet-mapping>
<servlet-name>
<url-pattern>
</servlet-mapping>
</web-app>
12.4 使用jetty-maven-plugin进行测试
在Web开发时, 无法避免打开浏览器对应用进行测试. 但是如果可以使用单元测试覆盖的修改, 尽量比较实用浏览器去验证, 因为无法自动化,效率差
现在也有一些web测试技术如Selenium来录制Web页面上的操作,生成脚本,然后自动化测试.
还是要强调的是web页面测试仅针对html,css,js的修改测试, 其他的代码如访问数据库, 请编写测试单元.
传统的Web测试方法要我们编译,测试,打包,部署, 是jetty-maven-plugin可以帮助我们节省时间,它会周期性的检查项目内容,
发现我们在IDE中修改代码后,就能自动把编译后的文件更新到Jetty容器, 就可以直接在Web页面测试了.
在WEB项目的POM中设置jetty-maven-plugin插件:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.1.6.v20100715</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds> <!-- 扫描项目的时间间隔,不配置不扫描,该热部署的功能没有了-->
<webAppConfig>
<contextPath>/test</contextPath> <!-- 这个配置有意思了, 浏览这个web项目的url是 http://hostname:port/test/-->
</webAppConfig>
</configuration>
</plugin>
此时我们可以在命令行运行命令 mvn org.mortbay.jetty:jetty-maven-plugin:7.1.0.RC1:run 来启动web项目了,可以在setting.xml中配置下,让命令短点
<pluginGroups>
<pluginGroup>org.mortbay.jetty</pluginGroup>
</pluginGroups>
现在可以使用 mvn jetty: run 运行了 ,默认的端口是8080
改变端口
mvn jetty:run -Djetty.port=9999
现在可以在浏览器中输入url http://localhost:9999/test/ 来测试应用了
jetty-maven-plugin插件其实还能配置很东西, 如自定义web.xml的位置,class文件的位置,web资源目录的位置等...
12.5 使用Cargo实现自动化部署
该工具是web容器的一个操作工具, 可以把项目部署到主流的web容器上, 和jetty-maven-plugin功能看起来类似, 但jetty他本身是一个容器, 帮助开发者本地测试项目.
而Cargo可帮助专职的测试人员使用简单的命令,把项目自动化的部署的测试环境等.
12.5.1 部署至本地Web容器
Cargo支持两种本地部署方式,如下:
standalone模式 : 从Web容器安装目录复制一份配置到用户指定目录,然后在此基础上部署,每次重构该目录会清空. 配置被重新生成
existing模式 : 用户需要指定web容器目录, 然后Cargo会把应用部署到对应容器的目录,
使用standalone模式部署应用至本地Web容器的POM插件配置:
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.0</version>
<configuration>
<container>
<containerId>tomcat6x</containerId>
<home>D:\...</home> <!-- web容器的位置-->
</container>
<configuration>
<type>standalone</type> <!-- 部署模式-->
<home>${project.build.directory}/tomcat6x</home> <!-- 复制容器中的配置到指定的位置,${project.build.directory} 这个表达式其实就是target/目录-->
<properties>
<cargo.servlet.port>8081</cargo.servlet.port> <!-- 配置web容器启动时的端口 ; 不配置默认是8080-->
</properties>
</configuration>
</configuration>
</plugin>
和jetty 插件类似, 需要在settings.xml中配置下,就可以使用简称来远行这个插件了
mvn cargo:start
使用existing模式部署应用至本地Web容器的POM插件配置: 使用该配置可以在tomcat的对应目录中看到我们的项目了
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.0</version>
<configuration>
<container>
<containerId>tomcat6x</containerId>
<home>D:\tomcat安装目录</home> <!-- web容器的位置-->
</container>
<configuration>
<type>existing</type> <!-- 部署模式-->
<home>D:\tomcat安装目录</home> <!-- 复制容器中的配置到指定的位置,${project.build.directory} 这个表达式其实就是target/目录-->
</configuration>
</configuration>
</plugin>
12.5.2 部署至远程web容器
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.0</version>
<configuration>
<container>
<containerId>tomcat6x</containerId>
<type>remote<type>
</container>
<configuration>
<type>runtime</type> <!-- 部署模式-->
<properties>
<cargo.remote.username>admin<cargo.remote.username> <!-- 远程容器的登入信息,实现热部署-->
<cargo.remote.password>admin123<cargo.remote.password>
<cargo.remote.url>http://localhost:8080/manager<cargo.remote.url>
</properties>
</configuration>
</configuration>
</plugin>
提示:
远程容器登入的信息每种服务器是不同的需要自己去看文档,使用下面命令部署项目到远程web容器
mvn cargo:redeploy
如果容器中已经部署了,当前应用,Cargo会先将其卸载,然后重新部署;
因为自动部署的配置辅助可以查Cargo的插件文档;可访问http://cargo.codehaus.org/Maven2+plugin
第十三章 版本管理
mavn还可以运行对应插件, 帮我们做版本的管理,如在svn中创建分支, 创建tag, 版本号升级等..
13.1 何为版本管理
我一个项目刚启动, 那么他就是1.0快照版 , 如开发完毕, 就是1.0发布版, 然后1.0快照版的POM便为1.1快照版,就这样迭代下去
什么时候项目可以称为发布版:
1. 所以自动化测试都能通过
2. 项目中没有配置任何快照版本的依赖
3. 项目中没有配置任何的快照的插件
4. 项目中保护的代码已经全部提交到版本控制系统中
如果项目这个版本好了, 那么可在版本库上打一个标签(tag), 这样我们的主干可以继续开发, 而当发布版发现bug, 那么可以根据tag来拉去代码后修改Bug(别忘了主干也要修改),再打包, 发布
某一个tag的代码会被单独放一个地方的, 不印象主干的开发, 也可以在某一刻, 只要签出tag的代码就能构建该版本的包, 从而可以比较各个版本之间的差异
小结:
当项目从快照版本更新为发布版本后,
用maven 构建一次项目(确保没问题) :mvn clean install
然后把更新的文件提交到版本库主干上: svn commit fileName -m 'update...'
接着在当前主干的状态打标签: svn copy -m "tar release1.0" https://.../project/trunk https://.../project/tags/1.0 (其实就是在服务器上再复制一份当前代码)
这样就完成了快照版的开发了
13.2 Maven 的版本号定义约定
例子说明:
1.3.4-bera-2
"1" 表示<主版本>, 升级它后, 前后2个版块的包可能是不兼容的
"3" 表示<次版本>, 升级它话, 不同版本的包都是兼容了, 在修复bug, 添加新功能,可以升级它
"4" 表示<增量版本>, 修改BUG 修改他,
"bera-2" 表示<里程碑版本号>; 某个版本的里程碑, 部分功能开发好了
<次版本><增量版本>是可选的
13.3 主干, 标签与分支
版本控制工具有 主干(trunk), 标签(tag)和分子(branch)的概念; Maven的版本管理会涉及到对版本控制工具的操作;下面对这3个概念的解释:
1.主干: 项目开发代码的主体,从项目开始到当前都处于活动的状态, 能获取项目最新的源代码以及几乎所有的变更历史.
2.分支: 从主干的某个点分离出来的代码拷贝, 可以在不影响主干的情况下在这里进行重大的BUG修复, 或做一些实验性的开发, 如果分支达到了预期的目地, 会将其合并(merge)到主干中.
3.标签: 用来标识主干或者分支的某个点的状态, 以代表项目某个稳定状态, 这个通常就是版本发布时的状态.
比方说: 当前的需求都开发完了,也发布了,给其打上标签如1.0版本, 那么开始开发2.0版本了, 有一天发现1.0版本有bug 要修复,只要按照标签来签出即可还原到当时的源码.
13.4 自动化版本发布
本节介绍在发布时需要做的一些工作,检查是否有未提交代码, 是否有快照依赖, 更新快照版本至发布版, 执行Maven构建,为源代码打标签等.
没有操作过还需要手动来一遍, 而Maven的插件Maven Release Plugin 可以自动化的搞定一切操作,
Maven Release Plugin 主要有三个目标, 它们分别为:
1.release:prepare 准备版本发布, 依次执行下列操作:
A) 检查项目是否有未提交的代码
B) 检查项目是否有快照版本依赖
C) 根据用户的输入将快照版本升级为发布版
D) 将POM中的SCM信息更新为标签地址
E) 基于修改后的POM执行Maven构建
F) 提交POM变更
G) 基于用户输入为代码打标签
H) 将代码从发布版升级为新的快照版
I) 提交POM变更
整个流程就是: 把1.0-SNAPSHOT的项目, 在svn中源代码打上标签1.0的版本>>更新1.0-SNAPSHOT项目的POM为1.0-RELEASE,修改其SCM的信息(svn的标签为1.0的)
>>根据新的POM执行Maven构建 >> 然后提交POM文件到svn >> 在把POM修改为1.1-SNAPSHOT; SCM再切换到主干>>提交新的POM
2.release:rollback: 回退release:prepare所执行的操作, 将POM回退至release:prepare之前的状态,并提交. 当时在SVN中已经生成的标签还在,需要手动删除
3.release:perform: 执行版本发布,签出release:prepare生成的标签中的源码, 并执行mvn deploy打包并部署构件至仓库.
使用该插件就要配置正确的SVN信息:(其他版本控制工具也可以,这里是那svn举例)
<project>
<!--主干的地址 -->
<scm>
<connection>scm:svn:http://192.168.1.103/app/trunk</connection> <!-- 只读的地址-->
<developerConnection>scm:svn:https://192.168.1.103/app/trunk</developerConnection> <!-- 读写的地址,使用了https保护-->
<url>http://192.168.1.103/account/trunk</url>
</scm>
</project>
该scm元素只配置了主干, 要让Maven Release Plugin对svn中的标签操作还要对其插件 进行设置
<plugin>
<groupId>org.apache.maven.pligins</groupId>
<artifactId>maven-release-plugin<artifactId>
<version>2.0</version>
<configuration>
<tagBase>https://192.168.1.103/app/tags/</tagBase>
</configuration>
</plugin>
提示:
执行release:prepare ,必须安装了svn 命令行工具, POM必须配置了可以用来部署的maven仓库,
因为release:perform会执行deploy操作,将构建发布到仓库中, 配置部署仓库可参考9.6.1节
一切就绪后,执行下面命令,准备发布版本:
mvn release:prepare //会坚持未提交diamond,快照依赖, 没出错, 会提示用户相同发布的版本号,svn标签的名称, 以及新快照的版本号,可以使用默认值
基于这些信息会更新POM,SCM,... 一系列的对POM修改提交, 对svn 打标签和提交新的POM
如果release:prepare完成, 缺发现问题,如标签名称配置错误, 可以使用release:rollback来回退发布;
如果是多模块同时release:prepare, 每个模块都要提示用户输入版本号,svn标签的名称, 以及新快照的版本号; 希望保持一致的话,可以使用以下命令
mvn release:repare -DautoVersionSubmodules=true
一切正常就可以使用下面命令把发布版的组件部署到Maven仓库, 同用户使用
mvn release:perform //这样一次发布部署发布版组件的过程算好了
运行该命令如果打包类型为jar , 那么还会把-sources.jar和-javadoc.jar 都发布到maven 仓库中, 因为该插件能触发maven-source-plugin, javadoc,deploy的插件执行
触发这些插件是在吵架POM中配置的有一个<activate>的子元素为preformReleas为true会触发, 所有我们平时也可以自己触发,如下:
mvn clean install -DperformRelease=true //因为平时开发没有必要生成这些源码包等, 所有只有在超级POM中配置了这个属性,
13.5 自动化创建分支
使用Maven Release Plugin的branch目标,可以完成自动化:
1. 检查本地有无未提交代码
2. 为分支更改POM的版本,例如从1.1.0-SNAPSHOT改为1.1.1-SNAPSHOT
3. 将POM中的SCM信息更新为分支地址
4. 提交以上更改
5. 将主干的代码复制到分支中
6. 修改本地代码使其回退到分支前的版本(用户可以指定新的版本)
7. 提交本地更改
需要为插件配置分支基础目录;如下:
<plugin>
<groupId>org.apache.maven.pligins</groupId>
<artifactId>maven-release-plugin<artifactId>
<version>2.0</version>
<configuration>
<tagBase>https://192.168.1.103/app/tags/</tagBase>
<branchBase>https://192.168.1.103/app/branchBase/</branchBase> <!-- 其实不是必须的,如果使用svn默认的tag,或branch,maven会自动检测-->
</configuration>
</plugin>
现在可以在项目目录下运行命令参加分支,(从主干上建一个分支)
mvn release:branch -DbranchName=1.1.x -DupdateBranchVersions=true -DupdateWorkingCopyVersions=false
参数解释:
-DbranchName=1.1.x 用来配置在svn中创建的分支的名称
-DupdateBranchVersions=true 表示为分支使用新的版本
-DupdateWorkingCopyVersions=false 表示不更新本地代码(即主干)的版本
然后会提示分支项目的版本,默认的可以用就Enter, 用户就会在源码仓库中找到Maven创建的分支如:https://192.168.1.103/app/branchBase/1.1.x
POM中的版本好也跟着更新了
13.6 GPG签名
Pretty Good Privacy(PGP)通常用来给电子邮件加密,解密已经提供签名, 以提高电子邮件交流的安全性. 本节介绍使用PGP技术为发布的Maven构件签名,加强安全性
13.6.1 GPG及其基本使用
GnuPG(简称GPG,http://www.gnupg.org)是PGP标准的一个免费实现,所有操作系统都能使用. 它能帮我们为文件生成签名,管理密钥, 验证签名等.
下载:
http://www.gnupg.org/download/ 验证安装:gpg --version
使用前准备
1. 生成密钥对: gpg --gen -key //要求你输入一些必要信息,名称,电子邮件地址密钥注解, 还有什么类型... 可以使用默认值, 还会要输入密码(可以不用)
2. 有了密钥可以查看了:公钥: gpg --list-keys // 会显示存储位置,等信息
私钥: gpg --list-secret -keys
3. 给任意文件签名(ASCII格式),(私钥有密码会要求输入密码)
gpg -ab temp.java //-a: 创建ASCII格式的输出, -b :创建一个独立的签名文件
会创建一个temp.java.asc的签名文件, 这时可以把这个文件和包,一起给用来, 用户导入了我的公钥就可以验证文件
4. 用户验证签名(需要导入公钥)
gpg -verify temp.java.asc
5. 把公钥上传到公钥服务器
gpg --keyserver hkp://pgp.mit.edu --send-keys C6EED57A //--keyserver:指定服务器id, --send-keys:公钥id(可以使用--list-keys,看到该ID)
6. 导入公钥
gpg --keyserver hkp://pgp.mit.edu --recv-keys C6EED57A //该公钥服务器是美国麻省理工学院提供的
13.6.2 Maven GPG Plugin
手动对Maven构建进行签名并部署到Maven仓库是一件耗时的活,值需要对Maven GPG Plugin提供几行简单的配置, 它就能办我们知道完成签名.
项目的POM, 插件配置:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
然后执行下面代码就能为构件签名并部署项目
mvn clean deploy -Dgpg.passphrase=yourpassphrase //参数的值是密码, 不给值运行时会要求输入
如果已经部署好的项目,可以使用它的一个目标来指定要签名的pom, jar,maven仓库url和ID
mvn gpg:sign-and-deploy-file \
-DpomFile=target/mayapp-1.0.pom \
-Dfile=target/myapp-1.0.jar \
-Durl=http://.../deploy/maven2/ \
-DrepositoryId=idValue
给项目构件签名, 只有正式发布的时候执行一次就够了, 所也在settings.xml中添加一个<profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name> <!--当Maven属性performRelease的值为true时触发这个profile-->
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId> <!-- 定义要触发的插件-->
<artifactId>maven-gpg-plugin</artifactId>
<version>1.0</version>
<executions> <!-- 插件执行配置-->
<execution>
<id>sign-artifacts</id>
<phase>verify</phase> <!-- 执行的Maven生命周期的阶段-->
<goals>
<goal>sign</goal> <!-- 插件要执行的目标定义-->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
由于Maven Release Plugin插件的bug, 执行release:perform执行签名可能导致进程永久挂起, 需要给其配置下面插件信息
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.0</version>
<configuration>
<tagBase>http://....</tagBase>
<branchBase>http://....</branchBase>
<mavenExecutorId>forked-path</mavenExecutorId> <!-- 主要配置这个防止BUG-->
</configuration>
</plugin>
第十四章 灵活的构件
可以根据开发环境,测试环境,和产品环境, 根据不同环境的配置(如数据库)等等配置, 来构建项目组件.
Maven内置了三大特性,即属性, Profile和资源过滤, 来帮助我们实现不同环境下够项目构建工作.
14.1 Maven 属性
前面讲过使用<properties>元素定义自己的属性(版本号就是使用的自定义属性值), Maven 其实有多种类型的属性,如下:
1. 内置属性: 主要有两个常用的内置属性---${basedir}表示项目根目录,就是保护pom文件的目录; ${version} 表示项目版本
2. POM属性: 就是在POM中定义的标签元素(可以获取你自己写在元素中的值), 例如:${project.artifactId} 对应<project><artifactId>元素的值,常用的POM属性如下(其中元素的默认值都是在超级POM中定义的):
A. ${project.build.sourceDirectory}: 项目的主源码目录,默认src/main/java/
B. ${project.build.testSourceDirectory}: 测试源码目录,src/test/java/
C. ${project.build.directory}: 项目构建输出目录,默认target/
D. ${project.outputDirectory}: 项目主代码编译输出目录, target/classes/
E. ${project.testOutputDirectory}: 项目测试代码编译输出目录, target/test-classes/
F. ${project.groupId}: 项目的groupId ; ${project.artifactId}: 项目artifactId; ${project.version}: 项目的version等价的有${version}
G. ${project.build.finalName}: 项目打包输出文件的名称, 默认为${project.artifactId}-${project.version} (之前讲过在<build>元素中加一个<finalName>元素可以修改这个默认值)
3. 自定义属性: 就是在POM中的<properties>,例子如下:
<properties>
<my.prop>hello</my.prop> <!-- 使用: ${my.prop}-->
</properties>
4. Settings属性: 和POM属性同理, 在settings.xml中定义的元素都可以通过元素名来获取其元素值, 如:${settings.localRepository}指向用户本地仓库的地址
5. Java系统属性: 所有java系统属性同可以使用maven属性引用, 例如:{user.home} 指向了用户目录, 可以同过mvn help:system 来查看所以java系统属性
6. 环境变量属性: 所有环境变量都可以属于以env.开头的Maven属性引用. 例如: ${env.JAVA_HOME},可以同过mvn help:system 来查看系统中的所有环境变量属性
正确使用可以简化POM的配置和维护工作, 举个例子:一个多模块的项目中, 所以模块的groupId和version都是一样的,那么我们里利用${project.groupId}和${peoject.version},来配置依赖模块的这2个值了
还有一些插件输出,可使用POM属性,来配置自己输出目录, 这样在不同环境下都不会有什么问题了,下面将结合profile和资源过滤,展示Maven能为构建提供更多的可能性
14.2 构建环境的差异
在不同的环境中, 项目的源码应该使用不同的方式进行构建, 最常见的就是数据库配置了, 如在src/main/resources/目录下放置如下内容的配置文本
数据库配置文件:
database.jdbc.driverClasss=com.mysql.jdbc.Driver
database.jdbc.connectionURL=...
database.jdbc.username=dev
database.jdbc.password=dev123
测试环境需要测试的数据库, 可能就不是这些值了,Maven就可以做的对不同环境的构建, 生产不同的配置文件.这就是资源过滤,(maven会找到对于的key,来替换value的值,生成该配置文件)
14.3 资源过滤
使用资源过滤使用的是maven-resources-plugin; 下面开始:
第一步: 需要对原有的配置文件进行修改,把本来写死的值改为maven属性值
database.jdbc.driverClasss=${db.driver}
database.jdbc.connectionURL=${db.url}
database.jdbc.username=${db.username}
database.jdbc.password=${db.password}
第二步: 设置一个profile元素, 在其中定义maven自定义属性, 来配置属性值
<profiles>
<profile>
<id>dev</id> <!-- 配置了一个开发环境的数据库配置, 如果是测试环境, 就在配一个类时的profile-->
<properties>
<db.driver>com.mysql.jdbc.Driver</db.driver>
<db.url>...</db.url>
<db.username>dev</db.username>
<db.password>dev123</db.password>
</properties>
</profile>
</profiles>
第三步: 对maven-resources-plugin插件进行配置,开启对资源文件的过滤
<resources> <!-- 开启对主资源的过滤-->
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource> <!-- 资源目录可能不只一个, 这里对该资源目录不过滤-->
<directory>${project.basedir}/src/main/sql</directory>
<filtering>false</filtering>
</resource>
</resources>
测试资源过滤开启
<testResources> <!-- 开启对主资源的过滤-->
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
最后一步: 配置都好了以后,运行命令来构建, 根据id激活一个profile的配置
mvn clean install -Pdev ; //-Pdev 就是激活id为dev的profile 的配置
此时我们的配置文件中的值会被替换为profile中的maven的属性值
14.4 Maven Profile
Maven引入了profile的概念, profile能够在构建的时候修改POM的一个子集(就好比动态配置POM); 例子和资源过滤中同一个:
配置2个profile过滤不同数据库环境
<profiles>
<profile>
<id>dev</id> <!-- 开发环境数据库配置-->
<properties>
<db.driver>com.mysql.jdbc.Driver</db.driver>
<db.url>...</db.url>
<db.username>dev</db.username>
<db.password>dev123</db.password>
</properties>
</profile>
<profile>
<id>test</id> <!-- 测试环境的数据库配置-->
<properties>
<db.driver>com.mysql.jdbc.Driver</db.driver>
<db.url>...</db.url>
<db.username>test</db.username>
<db.password>test123</db.password>
</properties>
</profile>
<profiles>
开发人员可以使用mvn命令后面跟-Pdev 来激活id=dev的profile, 测试使用-Ptest 来激活id=test的profile; 激活方式还有几种看下面
14.4.2 激活profile
Maven支持多种激活Profile的方式
1. 命令行激活: 使用的是 -P加profile的id,多个profil可以使用逗号分隔
mvn clean install -Pdev,test-classes
2. settings文件显式激活: 如果用户希望某个profile一直处于激活状态, 那么可以在settings.xml中配置activeProfiles元素,配置如下:
<settings>
<activeProfiles>
<activeProfile>dev</activeProfile> <!-- 这样就一直激活了这个id值的profile-->
</activeProfiles>
</settings>
在9.5节使用该方式默认激活了一个关于仓库配置的profile
3. 系统属性激活: 可以配置成单某个系统属性存在的时候,自动激活profile, 例子如下:
<profiles>
<profile>
<activation>
<property>
<name>test</name> <!-- 当系统属性test存在激活该profile-->
[ <value>x</value> ] <!-- 可选的用法,当系统属性test的值为xs时才激活-->
</property>
</activation>
//具体配置信息...
<profile>
</profiles>
如何使用:
mvn clean install -Dtest=x //使用-D 可以让maven任务这test是一个系统属性
4. 操作系统环境激活
可以根据操作系统环境激活;配置如下:
<profiles>
<profile>
<activation>
<file>
<missing>x.properties</missing>
<exists>y.properties</exists>
</file>
</activation>
//具体配置信息...
</profile>
</profiles>
5. 文件存在与否激活
Maven能根据项目中某个文件存在与否来决定是否激活profile; 配置如下:
<profiles>
<profile>
<activation>
<property>
<name>test</name> <!-- 当系统属性test存在激活该profile-->
[ <value>x</value> ] <!-- 可选的用法,当系统属性test的值为xs时才激活-->
</property>
</activation>
//具体配置信息...
<profile>
</profiles>
6. 默认激活
用户可以在定义profile的时候指定其默认激活:
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- 默认激活-->
</activation>
//具体配置信息...
</profile>
<profiles>
小结:
profile的配置被激活后, profile中的配置信息会覆盖默认的配置,
项目中有多个profile,他们的激活各异,可以通过下面的方式查看那个profile被激活了
mvn help:avtive-profiles
maven-help-plugin插件开可以列出所有的profile
mvn help:all-profiles
14.4.3 profile的种类
根据具体的需求, 可以在以下位置什么profile
1.POM.xml: 只对当前项目有效
2.用户settings.xml: 本机上该用户的所有Maven项目有效
3.全局setting.xml: Maven安装目录下的conf.settings.xml 对本机所有Maven的项目有效
4.profiles.xml(Maven2,可以忽略了, Maven3已经删除该特性,就是不饿profile都放该文件中)
不同类型的profile中可以声明的POM元素也是不同的,pom.xml中的profile能够随着pom一起提交到源码库, 能被安装在Maven仓库;是比较好的:
以下是POM中可以使用(配置)的元素:
<project>
<repositories>
<pluginRepositories>
<distributionManagement>
<dependecies>
<dependencyManagement>
<modules>
<properties>
<reporting>
<build>
<plugins>
<defaultGoal>
<resources>
<testResources>
<finalName>
</build>
</project>
因为POM会跟随项目一起维护,分发,所有在POM中的profile能配置很多的元素; 而外部的profile 则没有那么多的元素可供配置了;(外部的profile如果可以配置过多的元素,那么项目就会丧失移植性)
外部的profile的元素如下:
<project>
<repositories>
<pluginRepositories>
<properties>
</project>
14.5 Web资源过滤
前面讲到过使用资源过滤可以灵活配置不同环境的数据库的信息; 使用maven-war-pluin插件 web资源也可以过滤如js,css,图片;
下面的例子是根据不同客户端,来替换不同logo和主题样式的profile配置
<profiles>
<profile>
<id>client-a<id>
<properties>
<client.logo>a.jpg</client.logo>
<client.theme>red</client.theme>
<properties>
</profile>
<profile>
<id>client-b<id>
<properties>
<client.logo>b.jpg</client.logo>
<client.theme>blue</client.theme>
<properties>
</profile>
</profiles>
对Maven-war-plugin插件的配置
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1-beta-1</version>
<cofiguration>
<webResource>
<resource>
<filtering>true</filtering>
<directory>src/main/webapp</directory>
<includes>
<include>**/*.css</include>
<include>**/*.js</include>
</includes>
</resource>
</webResource>
</configuration>
</plugin>
使用时web资源过滤和资源过滤类似:
mvn clean instal -Pclinet-a
14.6 在profile中激活集成测试
很多项目有大量的单元测试和集成测试, 集成测试远行比较耗时, 可以配置profile来, 在有必要的时候来执行集成测试
利用的是TestNG中主的概念能够很好地支持单元测试和集成测试的分类; 可以使用如下的标注来表示一个测试方法属于哪类:
@Test(groups={"unit"}) //给某一个方法分类为单元测试
给某一个方法分类到集成测试组中
@Test(groups={"integration"})
接下来,配置测试插件默认只远行unit的测试, 配置一个profile 运行unit和integration两类测试
//插件的配置
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<groups>unit</groups>
</configuration>
</plugin>
//profile中对插件的配置
<profiles>
<profile>
<id>full</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<groups>unit,integration</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
该例子中,插件默认的行为是执行unit中的测试, 但激活了该profile后会覆盖插件原有的配置,执行profile的配置测试unit,integration的测试
如果对TestNG不熟悉可以回顾10.7节的内容
第十五章 生成 项目站点
第十六章 m2eclipse
第十七章 编写Maven插件
第十八章 Archetype
Maven Archetype 快速生成项目骨架
18.1.2 使用Archetype的一般步骤
mvn archetype:generate //运行命令行会以交换模式进行,
//然后有上千个maven的模板可选择,直接回车就是默认基础目录结构的模板
//接着会被要求输入3个坐标,和一个默认包名:
18.1.3 批处理方式使用Archetype
如果使用脚本形式使用Archetype的话, 也是可以的.代码如下:
mvn archetype:generate -B \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeAritfactId=maven-archetype-quickstart \