总结
1. 国内的maven镜像站!!
在~/.m2目录下的settings.xml文件中增加
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
2. pom 中使用 system 依赖需要注意
例如依赖了 ${project.basedir}/lib 目录下的 jar 包
<dependency>
<groupId>com.dingtalk.open</groupId>
<artifactId>client-sdk.api</artifactId>
<version>1.0.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/client-sdk.api-1.0.2.jar</systemPath>
</dependency>
打包时, system 依赖的 jar 包默认不会打到 target 中, 所以需要 maven-dependency-plugin
<build>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-dependency-plugin
</artifactId>
<versionRange>
[2.10,)
</versionRange>
<goals>
<goal>
copy-dependencies
</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>compile</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/lib</outputDirectory>
<includeScope>system</includeScope>
</configuration>
</execution>
</executions>
</plugin>
</build>
也可以使用 maven-war-plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<warName>${project.artifactId}</warName>
<webResources>
<resource>
<directory>lib/</directory>
<targetPath>WEB-INF/lib</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
一、安装Maven:
1. 在windows上安装Maven:
检查JDK安装
要求: jdk1.4及以上版本
echo %JAVA_HOME%;
java -version;
下载Maven
http://maven.apache.org/download.html
下载bin.zip,解压,
设置环境变量
增加 M2_HOME
Path增加%M2_HOME%\bin
验证
mvn -v
2. Unix上安装Maven:
检查JDK安装
要求: jdk1.4及以上版本
echo %JAVA_HOME%;
java -version;
下载 & 设置环境变量
mvnDirName="apache-maven-3.5.0"
mvnTarName="${mvnDirName}-bin.tar.gz"
wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.5.0/binaries/${mvnTarName}
tar xf ${mvnTarName}
# 做一个软链接,以后升级只需要更新软链接的目标即可
ln -s ${mvnDirName} apache-maven
echo "export M2_HOME=`pwd`/apache-maven" >> /etc/bashrc
echo "export PATH=\"\$PATH:`pwd`/apache-maven/bin\"" >> /etc/bashrc
验证
mvn -v
3. 安装目录分析
- bin/ --运行脚本,还有m2.conf,这是classwords的配置文件
- conf/ --有一个非常重要的文件settings.xml 不建议直接修改此文件,将此文件复制到~/.m2/目录下再修改 方便日后升级
- boot/ --只有一个文件,plexus-classwords-x.x.x.jar,这是一个类加载器框架 不必关心此文件
- lib/ --Maven运行时需要的类库
4. 设置HTTP代理
修改settings.xml,添加proxies元素:可以声明多个proxy,第一个被激活的proxy会生效
<settings>
...
<proxies>
<proxy>
<id>my-proxy</id>
<active>true</active>
<protocol>http</protocol>
<host>1.1.1.1</host>
<port>2222</port>
<!--
<username>***</username>
<password>***</password>
<nonProxyHosts>repository.mycom.com|*.google.com</nonProxyHosts>
-->
</proxy>
</proxies>
...
</settings>
5. Maven安装最佳实践:
5.1 设置MAVEN_OPTS环境变量:
mvn实际执行的是java命令,所以可以设置环境变量 MAVEN_OPTS,一般设置成 -Xms128m -Xmx512m
,因为Java的默认的最大可用内存往往不能够满足Maven运行的需要
尽量不要修改 mvn.bat或mvn 这两个执行脚本文件 因为升级时麻烦,且容易忘记
应该尽可能不修改安装目录下的文件
5.2 配置用户范围的 settings.xml
将 $M2_HOME/conf/settings.xml 复制到~/.m2/目录下修改
mkdir ~/.m2;
cp $M2_HOME/conf/settings.xml ~/.m2/
一个是全局的,一个是用户级的,还有就是不担心升级带来的配置更改
5.3 不要使用IDE内嵌的Maven:
在Eclipse的m2eclipse环境中,选择菜单
Windows=》Preferences=》Maven=》Installlation
添加一个外部Maven安装路径,并选中
三、maven使用入门
1. 编写POM
需要注意的是maven的Compiler插件默认只支持编译Java1.3,
因此需要配置该插件使其支持Java5.
如下所示:
<build>
<finalName>webfilemanage</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
2. 打包和运行:
默认打包生成的jar包是不能直接运行的,
带有main方法的类信息不会添加到manifest中(jar文件中的META-INF/MANIFEST.MF文件中没有Main-Class一行)
要想可以运行,需要借助maven-shade-plugin
配置文件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.juvenxu.mavenbook.HelloWorldCli</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
或者使用 maven-jar-plugin 插件增加 manifestEntries
(除了可以增加Main-Class之外,还可以增加其他任意的想加的内容,例如Premain-Class、Can-Redefine-Classes等)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Main-Class>
com.xxx.test.App
</Main-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
2.1 生成源码包
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
2.2 生成java-doc包
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
3. 使用archetype生成项目骨架:
mvn archetype:generate
四、坐标和依赖:
1. 坐标详解:
- groupId
- artifactId
- version
- packaging(默认是jar)
- classifier(可选)
一般构件名称为 artifactId-version[-classifier].packaging
2. 依赖的配置:
2.1 依赖的类型 type
对应于坐标中的packaging,默认是jar
2.2 依赖的范围 scope
首先需要知道maven在编译项目主代码的时候、在编译和执行测试的时候、和项目实际运行的时候
会分别使用3套classpath
而依赖的范围有
-
(1) compile(默认值.对于编译、测试、运行三种classpath都有效)
-
(2) test(只对测试classpath有效,编译和运行时不需要,典型例子是JUnit)
-
(3) provided(只对于编译和测试classpath有效,典型例子是servlet-api,运行时由于容器已经提供)
-
(4) runtime(对于编译无效、对于测试和运行有效,典型例子是JDBC驱动实现,项目主代码只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体的JDBC驱动)
-
(5) system(和provided一样,但是使用system范围的依赖必须通过systemPath元素显式地指定依赖文件的路径 由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此需要谨慎)
2.3 标记依赖是否可选optional
意思就是假设A依赖与B,B依赖于X和Y(可选依赖),
那么A不会有对X和Y的传递性依赖,A必须主动声明对X或Y的依赖
但是 应该避免使用可选依赖
应该把B分成B1和B2,分别对X和Y依赖
2.4 排除传递性依赖exclusions
内含多个<exclusion>元素,exclusion元素内指定groupId、artifactId、(不需要再指定version)表示排除的传递性依赖
然后再自己声明对某个构件的依赖,以使用自己的特定版本
2.5 导入依赖范围 import
一般此时type为pom,表示导入预定义好的依赖管理文件的内容
该依赖范围不会对三种classpath产生实际的影响
3. 传递性依赖:
假设A依赖B,B依赖C
-
如果B对C的依赖是compile,那么A对C的传递性依赖和A对B的依赖一致
-
如果B对C的依赖是test,那么A对C没有传递性依赖,这是很显然的,C只对测试B有用,对A当然没用
-
如果B对C的依赖是provided,那么只有A对B的依赖也是provided的时候,A对C才有传递性依赖(也是provided)
-
如果B对C的依赖是runtime,那么A对C的传递性依赖和A对B的依赖一致,除了A对B的依赖是compile的时候,此时A对C的传递性依赖是runtime
4. 依赖调解:
假设A->B->C->X(1.0)、A->D->X(2.0)
则
-
第一原则路径最近者优先
-
第二原则路径相同的,第一声明者优先
5. 最佳实践:
5.1 排除依赖:
5.2 归类依赖:
例子:
<properties>
<springframework.version>2.5.6</springframework.version>
</properties>
然后使用${}引用 相当于Java中的定义常量
<version>${springframework.version}</version>
5.3 查看当前项目的已解析依赖:
mvn dependency:list
查看依赖树:
mvn dependency:tree
分析项目依赖:
mvn dependency:analyze
结果中的WARNING有两个:
-
used undeclared dependencies 表示使用了没有显式声明的依赖(例如使用了传递性依赖的包中的类),有潜在的风险,应该进行显式声明
-
unsed declared dependencies表示未使用但声明了的,不能简单的删除其声明 因为执行测试和运行时需要的依赖他不能分析,例如spring-core是运行spring framework项目必须有的类库,显然不应该删除
五、仓库
1. 仓库的分类:
本地仓库和远程仓库
远程仓库又包括中央仓库、私服、其他公共库
(1) 本地仓库
本地仓库在~/.m2/reposity/
可以通过修改settings文件:
<localRepository>D:\mavenrepo\</localRepository>
执行下面的命令 会将生成的jar包安装到本地仓库
mvn clean install
(2) 中央仓库:
在 $M2_HONE/lib/maven-x.y.z-uber.jar 中的 org/apache/maven/model/pom-4.0.0.xml 中
有一个超级POM文件,其中定义了中央仓库的位置,
其中<snapshots>属性是false,表示不从中央仓库下载快照版本
(3) 私服:
即使是个人机器也应该建立私服,好处多多:
加速构建、
节省带宽、
部署第三方构件、
降低中央仓库的负荷
(4) 远程仓库的配置:
在项目的pom文件里声明
<repositories>
<repository>
<id>xx</id>
<name>xx</name>
<url>xx</url>
<releases>true|false</release>
<enabled>true | false </release>
<updatePolicy>daily| never | always | interval:X分钟 </updatePolicy>
<checksumPolicy>ignore | warn | fail </checksumPolicy>
<snapshots>true|false</release>
</repository>
</repositories>
(5) 远程仓库的认证:
认证信息必须在settings.xml中,
<servers>
<server>
<id>my-proj</id>
<username>repo-user</username>
<password>repo-pwd</password>
</server>
</servers>
2. 部署到远程仓库
需要配置pom.xml文件,配置 distributionManagement 元素,
如果需要认证,同上面一样
<distributionManagement>
<repository>
<id>xx</id>
<name>xx</name>
<url>xx</url>
</repository>
<snapshotRepository>
<id>xx</id>
<name>xx</name>
<url>xx</url>
</snapshotRepository>
</distributionManagement>
3. 快照版本:
强制让mvn检查更新
mvn clean install -U
例如 2.1-SNAPSHOT,在每次发布的时候Maven会自动为构件打上时间戳,也就是将SNAPSHOT替换成20091214.221414-13这样的时间戳,表示2009年12月14号22点14分14秒的第13次快照
4. 从仓库解析依赖的机制:
略
不推荐在依赖声明中使用LATEST和RELEASE,
尤其是LATEST,今天可能是1.3版本,明天可能就是1.4-SNAPSHOT,非常不可靠,
RELEASE还相对可靠,
Maven3以后不再支持使用LATEST和RELEASE,
如果不设置插件版本,则相当于RELEASE,
Maven只会解析最新的发布版本构件
不过即使这样,还是有潜在的风险
5. 镜像:
在settings.xml中配置:
<mirrors>
<mirror>
<id>xx</id>
<url>xx</url>
<mirrorOf>xx</mirrorOf>
</mirror>
</mirrors>
-
可以使用*,还可以使用 external:*表示匹配所有远程仓库
-
还可以 *,!repo1表示匹配所有仓库,repo1除外
镜像仓库会屏蔽被镜像仓库,因此要保证镜像仓库的稳定性
6. 仓库搜索服务:
六、生命周期和插件
1. 何为生命周期?
Maven的生命周期是抽象的,实际工作由插件完成
每个构件步骤都可以绑定一个或多个插件行为,而且Maven为大多数构建步骤编写并绑定了默认插件
例如
maven-compiler-plugin负责编译,
maven-surfire-plugin负责测试
用户可以配置插件定制构建行为,
甚至自己编写插件
2. 三套生命周期:
-
clean生命周期的目的是清理项目,
-
default生命周期的目的是构建项目,
-
site生命周期的目的是建立项目站点
每个生命周期都有一些阶段(phase),这些阶段是有顺序的,并且后面的phase依赖于前面的phase
而三套生命周期本身是相互独立的,用户可以只调用clean生命周期的某个阶段,而不会对其他生命周期产生影响
(1) clean生命周期
clean生命周期包含 pre-clean 、 clean 、 post-clean 三个阶段
(2) default生命周期
default生命周期包含 validate 、 initialize 、 generate-sources 、 process-sources(处理项目主资源文件,一般来说是对src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中)、 generate-resources 、 process-resources 、 compile 、 process-classes 、 generate-test-sources 、 process-test-sources 、 generate-test-resources 、 process-test-resources 、 test-compile 、 process-test-classes 、 test 、 prepare-package 、 package 、 pre-integration-test 、 integration-test 、 post-integration-test 、 verify 、 install 、 deploy
(3) site生命周期
site生命周期: pre-site 、 site 、 post-site 、 site-deploy
3. 命令行与生命周期:
- (1) mvn clean:调用clean生命周期的clean阶段,实际执行 pre-clean 和 clean 阶段
- (2) mvn test:调用default生命周期的test阶段
- (3) mvn clean install:调用clean生命周期的clean阶段和default生命周期的install阶段
4. 插件目标:
对于插件本身,为了能够复用代码,它往往可以完成多个任务
例如 maven-dependency-plugin,他能够基于项目依赖做很多事情
每个功能就是一个目标
maven-dependency-plugin 有10多个目标,
例如 dependency:analyze 、 dependency:tree等,
这是一种通用写法,前面是插件,冒号后面是目标
5. 插件绑定:
内置绑定,例如
maven-clean-plugin:clean 目标绑定了 clean 阶段,
maven-site-plugin:site 目标绑定了 site 阶段,
maven-site-plugin:deploy目标绑定了 site-deploy 阶段
还有很多生命周期的阶段没有绑定任何插件,因此也没有任何实际行为
(1) 自定义绑定:
<build>
<plugins>
<plugin>
<groupId><artifactId><version>
<executions>
<execution>
<id>task name</id>
<phase>verify</verify>
<goals>
<goal>jar-no-fork</goal>
</goals>
<configuration>
<echo>...</echo>
</configuration>
</execution>
</executions>
<configuration>
<source>xxx</source>
</configuration>
<plugin>
</plugins>
有时候,即使不通过phase指定生命周期阶段,插件目标也能绑定到生命周期中去,
通过下面的命令查看插件详细信息,可以看到插件的目标默认被绑定到的生命周期阶段.
mvn help:describe -Dplugin=org.apache.plugins:maven-source-plugin:2.1.1 -Ddetail
也就是说,当用户配置使用maven-source-plugin插件的jar-no-fork目标的时候,
如果不知道phase参数,则默认绑定到package阶段
多个插件被绑定到同一个生命周期阶段时按照声明的先后顺序执行
6. 插件配置:
(1) 命令行插件配置
使用 -D 参数,
例如 mvn install -Dmvn.test.skip=true 表示跳过测试代码的编译,
因为 maven-surfire-plugin 会判断 mvn-test-skip 参数,其值为 true 则跳过测试
( -DskipTests 是跳过测试)
(2) 全局插件配置
在POM中,在plugin中配置 <configuration> 的子元素
(3) POM中插件任务配置
见上面的配置文件
7. 获取插件信息:
(1) 在线插件信息
通过apache或其他网站查看插件文档等
(2) 使用命令
mvn help:describe -Dplugin=groupId:artifactId:version -Ddetail
可以简化成
mvn help:describe -Dplugin=compiler -Dgoal=compile -Ddetail
8. 从命令行调用插件:
mvn [options] [<goals>] [<phases>] -Dproperty=val
从mvn的语法可以看到,
通过mvn命令可以激活生命周期阶段
还支持从命令行调用插件目标,因为有些任务不适合绑定在生命周期上,例help:describe、dependency:tree
那么为什么help能表示maven-help-plugin?
9. 插件解析机制:
(1) 插件仓库:
在POM中 使用pluginRepositories和pluginRepository声明插件仓库
(2) 插件默认的groupId:
org.apache.maven.plugins
(3) 插件的版本:
首先内置的超级POM中为所有核心插件设置了版本,
如果不是核心插件,maven则检查所有仓库中的可用版本,
然后做出选择,为了减少潜在的风险,
建议一定要写明版本
(4) 解析插件前缀:
通过解析仓库元数据,groupId/maven-metadata.xml,
默认只检查org.apache.maven.plugins和org.codehaus.mojo两个groupId的元数据,
可以在settings里加入自己的pluginGroups=>pluginGroup让maven检查你的仓库元数据,
然后在元数据xml文件中设置artifactId的prefix,
然后就可以用prefix来代表artifactId
然后通过上面介绍的解析插件版本的方法解析到插件版本,就得到了完整的插件坐标
七、聚合与继承:
1. 聚合:
聚合的POM文件其packaging类型为pom,同时设置
<modules>
<module>xxx</module>
...
</modules>
module声明的是项目所在的文件夹路径及名称,所以聚合模块与其他模块的目录结构不一定是父子关系,
例如module可以设置成../xxx,那么它们就是平行关系
2. 继承:
<parent>
<groupId>chinaunicom.softwareri</groupId>
<artifactId>unifiedaccess</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../xx/xx</relativePath> <!-- 设置相对路径 -->
</parent>
可继承的POM元素有:groupId、version、description、organization、inceptionYear、url、developers、contributors、dsitributionMnagement、issueManagement、ciManagement、scm、mailingLists、properties、dependencyManagement、repositories、build、reporting
3. 依赖管理:
通过parent声明依赖管理来继承依赖是不合适的,因为不可能所有的模块都有相同的依赖
Maven提供的dependencyMangement元素既能让子模块继承父模块的依赖配置,又能保证子模块依赖的灵活性
使用dependencyManagement声明的依赖不会引入实际的依赖
但是他能够约束dependencies下的依赖使用
例如在parent中声明了dependencyManagement中的各种dependencies,
然后在子模块中声明dependencies时就不需要写version和scope,
这些已经在parent中统一声明过了
在dependencyManagement中声明的dependencies的scope还可以设置成import,type一般是pom,表示导入预定义好的依赖管理文件的内容
3.1 插件管理:
相应地maven提供了dependencyManagement元素帮助管理依赖
3.2 聚合与继承的关系:
聚合模块知道有哪些被聚合的模块,而被聚合的模块不知道被谁聚合
继承模块知道父模块的存在,但父模块不知道被谁继承,
它们唯一的共同点就是聚合模块和父模块的packaging都必须是pom 所以人们一般把他们结合在一起使用
4. 反应堆:
模块间的依赖关系构成一个有向非循环图(DAG=directed acyclic graph),不允许出现循环,
所以如果A依赖B,B又依赖A,Maven就会报错
(1) 裁剪反应堆
有时候仅仅想构件某几个模块
Maven提供的命令行选项
-
--also-make 同时构建所列模块的依赖模块 、
-
--also-make-dependents 同时构建依赖于所列模块的模块、
-
--projects <arg> 构建指定的模块,使用逗号分隔、
-
--resume-from <arg> 从指定的模块恢复反应堆,在完整的反应堆基础上指定从哪个模块开始构建
八、创建私服(Nexus)
http://www.sonatype.org/nexus/
1. 下载bundle或war文件,
bundle自带Jetty容器,因此不需要Web容器就可以直接启动Nexus
一般需要注意的是端口冲突,Nexus默认使用8081端口,编辑文件conf/plexus.properties,找到属性application-port,改端口号然后重启Nexus即可
2. 默认用户
默认的Nexus是匿名访问的,但匿名访问只有一些最基本的权限,要全面学习和管理Nexus,必须使用管理员登陆
默认admin/admin123
3. Nexus的仓库和仓库组
3.1 Nexus内置的仓库:
Nexus界面左边导航栏的repositories链接单击,在右边会看到列表,包含了所有类型的仓库
仓库有4种类型:group(仓库组)、hosted(宿主)、proxy(代理)、virtual(虚拟)
仓库的格式为maven2或maven1.
此外,仓库还有一个policy(策略),表示该仓库为发布版本仓库还是快照版本仓库,最后两列的值为仓库的状态和路径
这里不涉及maven1格式的仓库,由于virtual类型的仓库也是为了服务maven1格式,因此也被省略
-
maven central仓库代理maven中央仓库,其策略为release,因此只会下载和缓存发布版本构件
-
releases:这是一个策略为release的宿主类型仓库,用来部署组织内部的发布版本构件
-
snapshots:宿主类型仓库
-
3rd party:策略为release的宿主类型仓库,用来部署无法从公共仓库获得的第三方发布版本构件
-
public repositories:该仓库组将上述所有策略为release的仓库聚合并通过一致的地址提供服务
-
public snapshot repositories:
3.2 创建Nexus宿主仓库
Nexus界面左边导航栏的repositories链接单击,
在右边点add,选择hosted repository,
然后填入仓库id和名称 repository type表示仓库的类型
Provider用来确定该仓库的格式,一般来说,选择默认的maven2 repository
然后是policy,根据需要来配置该仓库是发布版构件仓库还是快照版构件仓库
3.3 创建Nexus代理仓库:
同上,选择proxy repository
(1) 创建仓库组:
最好将常用的仓库放在前面 , 以便尽快地访问到包含构件的仓库
3.4 配置maven从Nexus下载构件
- 可以在pom中配置repository和pluginRepository,但这样的配置只对当前项目有效
- 在settings中配置
<profiles>
<profile>
<id>
<repositories>
<repository> </repository>
</repositories>
</profile>
</profiles>
和类似的
<pluginRepository>
和
<activeProfiles>
配置完后maven还会不时地访问中央仓库,这就需要配置mirror,匹配*
3.5 部署构件到Nexus
使用maven,设置pom的distributionManagement 需要认证则在settings中设置server
3.6 手动部署第三方构件到Nexus
首先选择一个3rd party宿主仓库,然后选择artifact Upload
4. Nexus的权限管理:
Nexus是基于权限做访问控制的,服务器的每一个资源都有相应的权限来控制
管理员必须以role角色的方式赋予权限
例如要访问Nexus界面,就必须拥有status(读)权限
4.1 为项目分配独立的仓库:
首先建一个项目专用的仓库,
然后点击左边的repository targets链接,看右边的All(maven2),它的匹配值为.*,说明它可以匹配仓库下的任何路径
然后点击左边的privileges,在右边页面点击add-》repository target privilege
然后创建包含上述权限的角色,
然后将这个角色分配给项目团队成员
5. Nexus调度任务:
页面左边有scheduled tasks链接,点击add,选择调度任务类型,并配置运行方式
九、使用maven进行测试:
1. 运行
mvn test
测试报告内容
会根据你在test中写的测试类的数量,简单的说明,
运行了几个测试类,各运行了几个测试,运行情况 还有一个汇总的运行情况说明
测试类中使用 @Before @Test @After @Ignore等来标注方法
2. maven本身并不是单元测试框架
他只是在构建执行到特定生命周期阶段的时候,通过插件(maven-surefire-plugin)来执行JUnit或TestNG的测试用例
默认情况下,surefire的test目标会自动执行/src/test/java/下的所有符合命名模式的测试类,模式包括:
-
Test*.java
-
*Test.java
-
*TestCase.java
用户也就不用再定义测试集合(TestSuite)来聚合测试用例(TestCase)
需要注意的是以Tests结尾的测试类是不会自动执行的
当然也可以自定义要运行测试类的模式
还支持更高级的TestNG测试集合xml文件
3. 跳过测试:
mvn package -DskipTests,
也可以在pom中设置plugin的configuration的skipTests属性为true来长时间跳过测试
还可以使用 -Dmaven.test.skip=true 来跳过测试代码的编译,就是连测试代码编译都省了!
它同时控制了maven-compiler-plugin和maven-surefire-plugin两个插件的行为
在pom中的configuration属性是skip
实际上是compiler的testCompile目标和surefire的test目标都提供了一个参数skip用来跳过测试编译和测试运行,而这个参数的命令行表达式为maven.test.skip
4. 动态指定要运行的测试用例:
例如项目代码中有一个失败的测试用例,开发人员就会想要再次运行这个测试以获得详细的错误报告,使用
- -Dtest=xxxTest类,
- test=Random*Test,还支持Class1,Class2指定多个类
test参数的值必须匹配一个或者多个类,例如mvn test -Dtest 会报错误,可以使用-DfailIfNoTests=false来禁止这个报错功能
但是在命令行上没有相应的参数来指定要跳过测试的类,只能通过POM中配置
5. 包含与排除测试用例:
由于历史原因,某些测试类都是以Tests结尾的,
这就需要在POM中增加自动运行的模式,
配置surefire的includes中的include子元素的值为**/*Tests.java,
第一个**是表示任意路径,
第二个*表示匹配0或多个任意字符
类似地,还可以配置excludes的exclude子元素来排除那些不进行测试的类
6. 测试报告:
6.1 基本测试报告:
默认情况下,surefire会在项目的target/surefire-reports命令下生成两种格式的错误报告:
简单文本格式和与JUnit兼容的XML格式
6.2 测试覆盖率报告:
Cobertura是一个开源测试覆盖率统计工具,maven通过cobertura-maven-plugin与之集成,
可以使用简单的命令为maven项目生成测试覆盖率报告
例如:
mvn cobertura:cobertura
然后打开项目命令target/site/cobertura/下的index.html文件,就能看到测试覆盖率报告
7. 运行TestNG测试:
NG=NextGeneration下一代,支持测试组的概念,可以使用 @Test(groups={"util","medium"}) 在方法级别上对测试进行归类,这一点JUnit是做不到的
更多的自行查阅TestNG站点
8. 重用测试代码:
mvn package 默认不会把测试代码打包
要想把测试包打上,就要配置maven-jar-plugin将测试类打包,
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
test-jar的默认绑定生命周期阶段是package,所以声明了之后在package时就会运行这个goals
然后别人要引用这个包就可以在pom中声明这个包的type为test-jar就可以使用了
十一、使用maven构建web应用:
(1) 使用jetty-maven-plugin进行测试:
(2) 使用Cargo自动化部署:
十二、版本管理
1. 版本号定义约定
主版本.次版本.增量版本-里程碑版本
例如1.3.4-beta-2
2. 主干、标签与分支
标签用来标识主干或分支的某个点的状态,以代表项目的某个稳定状态,这通常就是版本发布时的状态
假设项目1.1.0发布之后,开始进入1.2.0-snapshot阶段,
可此时用户报告1.1.0版本有重大bug需要修复,
我们不能在主干中修bug,因为主干有太多变化,
我们也不能停止1.2.0-snapshot的开发,
因此这时候可以基于1.1.0创建一个1.1.1-snapshot分支,在这里进行bug修复,
然后为用户发布一个1.1.1增量版本,同时打上标签
当然,还不能忘了把bug修复涉及的变更合并到1.2.0-snapshot的主干中,主干在开发一段时间后,发布1.2.0版本
3. 自动化发布
如果不熟悉的话,建议一步一步地操作一遍,以得到最直观的感受:
检查是否有未提交的代码、是否有快照依赖、更新快照版至发布版、执行maven构建以及为源代码打标签等
当熟悉了版本发布流程后,我们就会希望借助工具将这一流程自动化
maven release plugin就提供了这样的功能
它主要有3个目标,分别为:
-
(1) release:prepare :这个目标将依次执行:检查项目是否有未提交的代码、检查项目是否有快照版本依赖、根据用户的输入将快照版本升级为发布版、将POM中的SCM信息更新为标签地址、基于修改后的POM执行maven构建、提交pom变更、基于用户输入为代码打标签、将代码从发布版升级为新的快照版、提交POM变更
-
(2) release:rollback :回退release:preare所执行的操作 将POM回退至release:prepare之前的状态,并提交 需要注意的是,该步骤不会删除release:prepare生成的标签,因此用户需要手动删除
-
(3) release:perform: 执行版本发布 签出release:prepare生成的标签中的源代码,并在此基础上执行mvn deploy命令打包并部署构件到仓库中
要为项目发布版本,首先需要为其添加正确的版本控制系统信息,
这是因为maven release plugin需要知道版本控制系统的主干、标签等地址信息才能执行相关的操作
一般配置项目的SCM信息如下所示:
<project>
...
<scm>
<connection>scm:svn:http://xxx/x/x</connection>
<developerConnection>scm:svn:https://x/x/x</developerConnection>
<url>http://x/x/x</url>
</scm>
</project>
- connection表示一个只读的scm地址,
- developerConnection表示可写的scm地址,
- url表示可以在浏览器中访问的scm地址
- Connection和developerConnection必须是scm开头,然后是版本控制工具类型,然后才是地址
还需要配置release plugin的tagBase配置熟悉
4. 自动化创建分支:
maven release plugin的branch目标
5. gpg签名:
GnuPG 是 PGP标准的一个免费实现
maven-gpg-plugin的sign目标在verify阶段执行
还有就是gpg签名只在发布版发布的时候运行,可以写一个profile
十三、灵活的构建
1. maven属性:
最简单的就是通过<properties>元素自定义属性
然后使用${}来引用
除了这种属性之外,还有:
(1) 内置属性:
例如
${basedir}表示项目根目录,即表示包含pom.xml的目录,
${version}表示项目版本=${project.version}
(2) POM属性
pom文件内对应元素的值
例如
${project.actifactId}就表示<project>下<artifactId>元素的值
${project.build.sourceDirectory}表示项目主源码目录,即/src/main/java ${project.build.testSourceDirectory}表示src/test/java
${project.build.directory}表示target
${project.outputDirectory}表示target/classes
${project.build.finalName}
它们中一些属性的默认值都是在超级POM中定义的
(3) Settings属性
引用settings.xml文件中的值
例如
${settings.localRepository} 表示指向用户本地仓库的地址
(4) Java系统属性
例如
${user.home}指向用户目录
可以使用mvn help:system来查看所有的java系统属性
(5) 环境变量属性
例如
${env.JAVA_HOME} 表示指代JAVA_HOME环境变量的值
2. 构建环境的差异:
定义profile,为资源文件开启过滤(也就是将资源文件中的属性替换,默认就不替换属性的),
还可以定义多个资源文件路径
通过 mvn clean install -PprofileId
来激活profile
示例
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/*.xml</exclude>
</excludes>
</resource>
...
</resources>
3. maven profile:
4. 激活profile:
(1) 命令行激活
mvn clean install -PprofileId1,profileId2
(2) settings文件显式激活:
<activeProfiles>
<activeProfile></activeProfile>
</activeProfiles>
(3) 系统属性激活:
设置profile的属性
<activation>
<property>
<name>xx</name>
<value>xx</value>
</property>
</activation>
表示当某属性存在或某属性的值为xx的时候激活
(4) 操作系统环境激活:
<activation>
<os>
<name>Windows XP</name>
<family>Windows</family>
<arch>x86</arch>
<version>5.1.2600</version>
</os>
</activation>
这些属性的值可以查看环境中的系统属性os.name、os.arch等获得
(5) 文件存在与否激活
<activation><file><missing><exists>
(6) 默认激活:
<activation>
<activeByDefault>true</activeByDefault>
</activation>
需要注意的是,如果POM中有任何一个profile通过以上其他任意一种方式被激活了,所有的默认激活配置都会失效!
(7) 查看激活的profile和所有的profile:
mvn help:active-profiles
mvn help:all-profiles
5. profile的种类
- pom.xml
- settings.xml
pom中的profile可以使用的元素非常多,
而settings中的因为不能随pom一起发布,
所以可使用的元素很少,包括repositories、pluginRepositories、properties
6. web资源过滤:
使用maven-war-plugin的configuration 的 <webResources>定义webresource资源文件
<webResources>
<resource>
<filtering>true</filtering>
<directory>src/main/webapp</directory>
<includes>
<include>**/*.css</include>
<include>**/*.js</include>
</includes>
</resource>
</webResources>
7. 使用profile激活集成测试:
一般单元测试执行时间短,而集成测试执行时间长,虽然我们应尽量执行所有的测试,但是当集成测试较多的时候高频率运行它们就变得不现实
所以,较高频率执行单元测试、较低频率执行集成测试是不错的选择
使用TestNG中的组的概念能很好的支持单元测试和集成测试的分类标记 @Test(groups={"unit"}) @Test(groups={"integration"})
然后在pom中配置surefire的configuration的<groups>unit</groups>,再写一个profile,指定groups为unit,integration.
然后在hudson中每隔15分钟检查一次更新,有更新则默认执行单元测试的构建,然后配置一个定时任务,每天执行2次,执行一个激活指定profile的构建
十四、生成项目站点:
将pom的信息用web的形式显示 maven-site-plugin
十五、m2eclipse:
十六、Archetype:maven-archetype-plugin插件
1. 使用:
mvn archetype:generate:
2. 批处理方式:
mvn archetype:generate -B -DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeAritifactId=maven-archetype-quickstart \
-DgroupId=...
这个例子中Archetype的坐标为org.apache.maven.archetypes:maven-archetype-quickstart
3. 常用Archetype介绍:
maven中央仓库中有200多个Archetype 还有很多没有发布到中央仓库的 这里介绍几个常用的
(1) maven-archetype-quickstart:可能是最常用的
(2) maven-archetype-webapp:
网友评论