maven打包编译

3.3 编写测试代码
为了使项目结构保持清晰,主代码与测试代码应该分别位于独立的目录中。
Maven项目中默认的主代码目录是src/main/java,对应地,Maven项目中默认的测试代码目录是src/test/java。
因此,在编写测试用例之前,我们先创建该目录。
在Java世界中,由Kent Beck和Erich Gamma建立的JUnit是事实上的单元测试标准。
要使用JUnit,我们首先需要为Hello World项目添加一个JUnit依赖,修改项目的POM如代码清单3-3:
代码清单3-3:为Hello World的POM添加依赖
Java代码

<?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/maven-v4_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>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</project> 

 

代码中添加了dependencies元素,该元素下可以包含多个dependency元素以声明项目的依赖,
这里我们添加了一个依赖——groupId是junit,artifactId是junit,version是4.7。
前面我们提到groupId、artifactId和version是任何一个Maven项目最基本的坐标,
JUnit也不例外,有了这段声明,Maven就能够自动下载junit-4.7.jar。
也许你会问,Maven从哪里下载这个jar呢?在Maven之前,我们可以去JUnit的官网下载分发包。
而现在有了Maven,它会自动访问中央仓库(http://repo1.maven.org/maven2/),下载需要的文件。
读者也可以自己访问该仓库,打开路径junit/junit/4.7/,就能看到junit-4.7.pom和junit-4.7.jar。
上述POM代码中还有一个值为test的元素scope,scope为依赖范围,
若依赖范围为test则表示该依赖只对测试有效,换句话说,测试代码中的import JUnit代码是没有问题的,
但是如果我们在主代码中用import JUnit代码,就会造成编译错误。
如果不声明依赖范围,那么默认值就是compile,表示该依赖对主代码和测试代码都有效。
配置了测试依赖,接着就可以编写测试类,
回顾一下前面的HelloWorld类,现在我们要测试该类的sayHello()方法,
检查其返回值是否为“Hello Maven”。在src/test/java目录下创建文件,
其内容如代码清单3-4:
代码清单3-4:Hello World的测试代码
Java代码

package com.juvenxu.mvnbook.helloworld;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class HelloWorldTest
{
@Test
public void testSayHello()
{
HelloWorld helloWorld = new HelloWorld();

String result = helloWorld.sayHello();

assertEquals( "Hello Maven", result );
}
}

 
一个典型的单元测试包含三个步骤:
一,准备测试类及数据;
二,执行要测试的行为;
三,检查结果。
上述样例中,我们首先初始化了一个要测试的HelloWorld实例,
接着执行该实例的sayHello()方法并保存结果到result变量中,
最后使用JUnit框架的Assert类检查结果是否为我们期望的”Hello Maven”。
在JUnit 3中,约定所有需要执行测试的方法都以test开头,这里我们使用了JUnit 4,
在JUnit 4中,需要执行的测试方法都应该以@Test进行标注。
测试用例编写完毕之后就可以调用Maven执行测试,运行 mvn clean test :
Java代码
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Hello World Project
[INFO] task-segment: [clean, test]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] Deleting directory D:\git-juven\mvnbook\code\hello-world\target
[INFO] [resources:resources {execution: default-resources}]
Downloading: http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.pom
1K downloaded (junit-4.7.pom)
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Compiling 1 source file to D: \code\hello-world\target\classes
[INFO] [resources:testResources {execution: default-testResources}]
Downloading: http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.jar
226K downloaded (junit-4.7.jar)
[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] Compiling 1 source file to D:\ code\hello-world\target\test-classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
D:\code\hello-world\src\test\java\com\juvenxu\mvnbook\helloworld\HelloWorldTest.java:[8,5] -source 1.3 中不支持注释
(请使用 -source 5 或更高版本以启用注释)
@Test
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch



不幸的是构建失败了,不过我们先耐心分析一下这段输出(为了本书的简洁,一些不重要的信息我用省略号略去了)。
命令行输入的是mvn clean test,而Maven实际执行的可不止这两个任务,
还有clean:clean、resources:resources、compiler:compile、resources:testResources以及compiler:testCompile。
暂时我们需要了解的是,在Maven执行测试(test)之前,
它会先自动执行项目主资源处理,主代码编译,测试资源处理,测试代码编译等工作,
这是Maven生命周期的一个特性,本书后续章节会详细解释Maven的生命周期。
从输出中我们还看到:Maven从中央仓库下载了junit-4.7.pom和junit-4.7.jar
这两个文件到本地仓库(~/.m2/repository)中,供所有Maven项目使用。
构建在执行compiler:testCompile任务的时候失败了,Maven输出提示我们需要使用-source 5或更高版本以启动注释,
也就是前面提到的JUnit 4的@Test注解。这是Maven初学者常常会遇到的一个问题。
由于历史原因,Maven的核心插件之一compiler插件默认只支持编译Java 1.3,
因此我们需要配置该插件使其支持Java 5,见代码清单3-5:
代码清单3-5:配置maven-compiler-plugin支持Java 5
Java代码

<project>
…
<build>
<plugins>
<plugin>
	<artifactId>maven-compiler-plugin</artifactId>
	<version>3.1</version>
	<configuration></configuration>
</plugin>
</plugins>
</build>
…
</project> 

 

该POM省略了除插件配置以外的其他部分,我们暂且不去关心插件配置的细节,只需要知道compiler插件支持Java 5的编译。
现在再执行mvn clean test,输出如下:
Java代码

[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] Compiling 1 source file to D: \code\hello-world\target\test-classes
[INFO] [surefire:test {execution: default-test}]
[INFO] Surefire report directory: D:\code\hello-world\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.juvenxu.mvnbook.helloworld.HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.055 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------


我们看到compiler:testCompile任务执行成功了,测试代码通过编译之后在target/test-classes下生成了二进制文件,
紧接着surefire:test任务运行测试,surefire是Maven世界中负责执行测试的插件,
这里它运行测试用例HelloWorldTest,并且输出测试报告。显然,我们的测试通过了——BUILD SUCCESSFUL。
---------------------------------------
3.4 打包和运行

将项目进行编译、测试之后,下一个重要步骤就是打包(package)。
Hello World的POM中没有指定打包类型,使用默认打包类型jar,
我们可以简单地执行命令 mvn clean package 进行打包,
可以看到如下输出:
Java代码
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] [jar:jar {execution: default-jar}]
[INFO] Building jar: D:\code\hello-world\target\hello-world-1.0-SNAPSHOT.jar
[INFO]
--------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL

类似地,Maven会在打包之前执行编译、测试等操作。
这里我们看到jar:jar任务负责打包,实际上就是jar插件的jar目标将项目主代码打包成一个名为hello-world-1.0-SNAPSHOT.jar的文件,
该文件也位于target/输出目录中,它是根据artifact-version.jar规则进行命名的,
如有需要,我们还可以使用finalName来自定义该文件的名称,这里暂且不展开,本书后面会详细解释。
至此,我们得到了项目的输出,如果有需要的话,就可以复制这个jar文件到其他项目的Classpath中从而使用HelloWorld类。
但是,如何才能让其他的Maven项目直接引用这个jar呢?
我们还需要一个安装的步骤,执行 mvn clean install:
Java代码

[INFO] [jar:jar {execution: default-jar}]
[INFO] Building jar: D: \code\hello-world\target\hello-world-1.0-SNAPSHOT.jar
[INFO] [install:install {execution: default-install}]
[INFO] Installing D:\code\hello-world\target\hello-world-1.0-SNAPSHOT.jar to
C:\Users\juven\.m2\repository\com\juvenxu\mvnbook\hello-world\1.0-SNAPSHOT\hello-world-1.0-SNAPSHOT.jar
[INFO]
------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL

在打包之后,我们又执行了安装任务install:install,
从输出我们看到该任务将项目输出的jar安装到了Maven本地仓库中,
我们可以打开相应的文件夹看到Hello World项目的pom和jar。
之前讲述JUnit的POM及jar的下载的时候,我们说只有构件被下载到本地仓库后,才能由所有Maven项目使用,
这里是同样的道理,只有将Hello World的构件安装到本地仓库之后,其他Maven项目才能使用它。
我们已经将体验了Maven最主要的命令:mvn clean compile、mvn clean test、mvn clean package、mvn clean install。
执行test之前是会先执行compile的,执行package之前是会先执行test的,
而类似地,install之前会执行package。我们可以在任何一个Maven项目中执行这些命令,而且我们已经清楚它们是用来做什么的。

到目前为止,我们还没有运行Hello World项目,不要忘了HelloWorld类可是有一个main方法的。
默认打包生成的jar是不能够直接运行的,因为带有main方法的类信息不会添加到manifest中
(我们可以打开jar文件中的META-INF/MANIFEST.MF文件,将无法看到Main-Class一行)。
为了生成可执行的jar文件,我们需要借助maven-shade-plugin,配置该插件如下:
Java代码

<plugin> 
	<artifactId>maven-shade-plugin</artifactId>
	<version>2.2</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.mvnbook.helloworld.HelloWorld</mainClass> mainclass类
					</transformer>
				</transformers>
			</configuration>
		</execution>
	</executions>
</plugin>

 

plugin元素在POM中的相对位置应该在<project><build><plugins>下面。
我们配置了mainClass为com.juvenxu.mvnbook.helloworld.HelloWorld,项目在打包时会将该信息放到MANIFEST中。
现在执行 mvn clean install ,待构建完成之后打开target/目录,
我们可以看到hello-world-1.0-SNAPSHOT.jar和original-hello-world-1.0-SNAPSHOT.jar,
前者是带有Main-Class信息的可运行jar,后者是原始的jar,
打开hello-world-1.0-SNAPSHOT.jar的META-INF/MANIFEST.MF,可以看到它包含这样一行信息:
Main-Class: com.juvenxu.mvnbook.helloworld.HelloWorld
现在,我们在项目根目录中执行该jar文件:
D: \code\hello-world>java -jar target\hello-world-1.0-SNAPSHOT.jar
Hello Maven
控制台输出为Hello Maven,这正是我们所期望的。
本小节介绍了Hello World项目,侧重点是Maven而非Java代码本身,
介绍了POM、Maven项目结构、以及如何编译、测试、打包,等等。
------------------------------------------------------
3.5 使用Archetype生成项目骨架
Hello World项目中有一些Maven的约定:
在项目的根目录中放置pom.xml,
在src/main/java目录中放置项目的主代码,
在src/test/java中放置项目的测试代码。
我们称这些基本的目录结构和pom.xml文件内容称为项目的骨架,
当你第一次创建项目骨架的时候,你还会饶有兴趣地去体会这些默认约定背后的思想,
第二次,第三次,你也许还会满意自己的熟练程度,但第四、第五次做同样的事情,就会让程序员恼火了,
为此Maven提供了Archetype以帮助我们快速勾勒出项目骨架。
还是以Hello World为例,我们使用maven archetype来创建该项目的骨架,离开当前的Maven项目目录。
如果是Maven 3,简单的运行:
mvn archetype:generate
我们实际上是在运行插件maven-archetype-plugin,
注意冒号的分隔,其格式为 groupId:artifactId:version:goal ,
org.apache.maven.plugins 是maven官方插件的groupId,maven-archetype-plugin
是archetype插件的artifactId,2.0-alpha-5 是目前该插件最新的稳定版,generate是我们要使用的插件目标。
紧接着我们会看到一段长长的输出,有很多可用的archetype供我们选择,
包括著名的Appfuse项目的archetype,JPA项目的archetype等等。
每一个archetype前面都会对应有一个编号,同时命令行会提示一个默认的编号,
其对应的archetype为maven-archetype-quickstart,我们直接回车以选择该archetype,
紧接着Maven会提示我们输入要创建项目的groupId、artifactId、 version、以及包名package,如下输入并确认:
Java代码
Define value for groupId: : com.juvenxu.mvnbook
Define value for artifactId: : hello-world
Define value for version: 1.0-SNAPSHOT: :
Define value for package: com.juvenxu.mvnbook: : com.juvenxu.mvnbook.helloworld
Confirm properties configuration:
groupId: com.juvenxu.mvnbook
artifactId: hello-world
version: 1.0-SNAPSHOT
package: com.juvenxu.mvnbook.helloworld
Y: : Y

Archetype插件将根据我们提供的信息创建项目骨架。
在当前目录下,Archetype插件会创建一个名为hello-world(我们定义的artifactId)的子目录,
从中可以看到项目的基本结构:基本的pom.xml已经被创建,里面包含了必要的信息以及一个junit依赖;
主代码目录src/main/java已经被创建,在该目录下还有一个Java类com.juvenxu.mvnbook.helloworld.App,
注意这里使用到了我们刚才定义的包名,而这个类也仅仅只有一个简单的输出Hello World!的main方法;
测试代码目录src/test/java也被创建好了,并且包含了一个测试用例com.juvenxu.mvnbook.helloworld.AppTest。
Archetype可以帮助我们迅速地构建起项目的骨架,
在前面的例子中,我们完全可以在Archetype生成的骨架的基础上开发Hello World项目以节省我们大量时间。
此外,我们这里仅仅是看到了一个最简单的archetype,
如果你有很多项目拥有类似的自定义项目结构以及配置文件,
你完全可以一劳永逸地开发自己的archetype,然后在这些项目中使用自定义的archetype来快速生成项目骨架

---------------------------------------------------------------------------------------------------

1.7java-maven书籍

Maven实战 许晓斌 (2012-12出版)

猜你喜欢

转载自zxc3375.iteye.com/blog/2009521
今日推荐