Maven不仅是一个新建工具。如果正确使用,它还能帮助你管理项目及进行配置。下面我们来进行说明。
在这个系列以前的文章中,我们讨论了“开发Swing应用程序”和“用网络服务功能开发一个混合Swing应用程序”。现在,我们把它们结合起来,讨论如何将这些代码应用到一个能够配置到服务器上的网络应用程序中。
我们主要改变了建立应用程序所使用的方法。之前我们用过Ant,但这次我们换用Apache Maven,它具有更强的生命周期导向性,现在已发布第二版。虽然我们用Maven代替Ant,但不要认为Maven只是一个建立工具。它还可通过最佳实践模式对项目进行管理。如果我们首先安装Maven,再获取本月的ToDoTasks源代码,将会更加方便。
首先你会发现目录树相当简单。在顶部有一个src目录和一个pon.xml文件,这个文件即项目对象模型(POM),它保存所有与项目有关的信息。POM文件告诉Maven如何建立一个它所称的artifact,它有一个名称,在jar或war文件中称为组和版本和映射。因此POM最先拥有的是我们正在建立的artifact信息:
<?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> |
这是强制性的开端部分。我们打开一个项目元素,然后就会出现一个必需的modelVersion元素,它说明我们正在使用哪个POM版本。现在进入artifact信息。
<groupId>uk.builder</groupId>
<artifactId>todotasks</artifactId>
groupID是一个针对一串artifact的唯一标识符。在上例中,它为uk.builder。artifactID是组中artifact的唯一名称。它们共同为artifact命名,而非说明它的版本。
<version>2.0-SNAPSHOT</version>
这是我们的版本。-SNAPSHOT是一个说明“开发中”版本的Maven实例。现在,虽然我们有了项目的工作名称,我们还需要一个显示名称。
<name>ToDoTasks</name>
在需要显示artifact的名称时,Maven将使用这个名称。现在讨论最重要的部分:
<packaging>war</packaging>
<packaging>和</packaging>之间的元素可以为值jar、war或ear。我们正在建立一个网络应用程序,所以我们使用war。这不只是说明我们封装artifact的方法(初看起来是这样),实际上它影响artifact的整个生命周期。
现在,我们暂停讨论POM文件,再来了解src目录。
src -+-> main -+-> java -> com -> builder -> uk -> todotasks
||
|+-> webapp -+-> META-INF
||
|+-> WEB-INF
|
+-> test -+-> java -> com -> builder -> uk -> todotasks
Maven项目的src目录在一个主目录和其它子目录下保存所有源代码,java代表Java源代码,webapp代表网络应用程序代码。测试代码是唯一的例外,它保存在test目录下的一个平行目录中。你会发现其中没有jar文件的库目录,这是因为Maven并不需要它。
Maven根据jar/war/ear文件贮藏库来考虑问题,每个库都有各自的组ID,artifact ID和版本,这些文件称之为artifact。在本地系统中,你拥有自己的贮藏库,它一般位于主目录的.m2目录下。Maven新建一个项目的目的是建立一个artifact,它可以安装到这个贮藏库中,以方便其它项目使用。还有远程贮藏库,Maven可通过它们获得artifact拷贝,再把它们安装到本地贮藏库中,同样是为了方便其它项目使用。
Maven通过pom.xml文件中的另一个区域——依赖区域——来了解需要哪些artifact。
<dependencies> <dependency> <groupId>jdbm</groupId> <artifactId>jdbm</artifactId> <version>1.0</version> </dependency> |
这是一个获取jdbm的简单依赖。默认情况下,Maven通过访问http://www.ibibio.ort/maven2/来获得依赖,如果你查看jdbm/jdbm/1.0/目录,就会发现jdbm-1.0.jar文件,Maven将恢复它;以及一个简短的jdbm pom.xml文件,它列举artifact本身拥有的任何依赖。你可以手动浏览ibibio贮藏库或使用MVN Registry这样的网站来搜索贮藏库。
下一个要讨论的依赖是dwr,但你在ibibio贮藏库中找不到它。你必须将它安装到我们自己的本地贮藏库中。
<dependency> <groupId>dwr</groupId> <artifactId>dwr</artifactId> <version>2.0M3</version> </dependency> |
下一个依赖为Java Servlet API。在ibibio库中可以找到它,但我们只有在建立项目的时候才需要它;因为当我们配置一台网络服务器时,servlet API已经存在。
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4-20040521</version> <scope>provided</scope> </dependency> |
scope说明何时需要这个artifact,因此“provided”说明artifact由运行时间环境提供。另一个scope类型为“test”,它说明只有在测试时需要artifact。例如,我们这样包括Junit:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> |
现在,Maven项目知道它会需要哪些贮藏库。我们差不多为新建项目做好准备。Maven还需要了解两件事:用Java 1.5建立源代码并运行它。这二步在build块中完成。
<build>
<finalName>todotasks</finalName>
首先,在build块中是我们希望为代码取的最终名称。然后我们为我们希望使用的插件定义设置。插件是Maven扩张自身的方式,你不必安装它们,只需要使用就行。Maven将从自己的贮藏库中恢复它们。我们设置的第一个插件是maven编译器插件。我们只需通过设定两个设置属性——源版本和目标版本——告诉它正在编译Java 1.5代码。
<plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.0</version> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> |
最后,我们将使用一个叫做Cargo的插件,它把我们建立的网络应用程序传送到一个能够运行它的容器中。
<plugin> <groupId>org.codehaus.cargo</groupId> <artifactId>cargo-maven2-plugin</artifactId> </plugin> </plugins> </build> </project> |
现在我们已经为建立项目做好准备。记得Maven的目的是将artifact安装到贮藏库中,因此如果我们要它那样做,它会运行所有必要的步骤。
$ mvn install
…
现在Maven要下载许多内容,因为它要从中央贮藏库提取所有插件和必要的artifact。但是,我们还遗漏了一个artifact——DWR artifact。Maven最终将会出错,并显示下面这段内容丰富的错误信息:
[INFO] --------------------------------------------------------------- [ERROR] BUILD ERROR [INFO] --------------------------------------------------------------- [INFO] Failed to resolve artifact. Missing: ---------- 1) dwr:dwr:jar:2.0M3 Try downloading the file manually from the project web site. Then, install it using the command: mvn install:install-file -DgroupId=dwr -DartifactId=dwr -Dversion=2.0M3 -Dpackaging=jar -Dfile=/path/to/file Path to dependency: 1) uk.builder:todotasks:war:2.0-SNAPSHOT 2) dwr:dwr:jar:2.0M3 ---------- 1 required artifact is missing. -- |
因此我们需要下载DWR2.0M3并把它安装到贮藏库中。DWR主网站上没有DWR2.0M3,你可以访问Java.net并在那里下载dwr.jar文件,然后运行Maven建议的命令。现在我们可以再试着运行安装。
$ mvn install
…
然后会有更多下载,代码编译、单元测试运行、网络应用程序装配、通过它建立的war文件和安装到本地Maven贮藏库的war文件。所有这些工作的结果被存放到新建的目标目录中。当然,我们希望现在就运行应用程序。这时就要用到cargo插件。在默认情况下,cargo会下载并安装内置的Jetty 5.x网络服务器,并运行主war文件。要实现这一点,我们调用cargo的start goal。
$ mvn cargo:start
接着再下载所需的artifact,最终以下列的信息结束:
[INFO] [beddedLocalContainer] Jetty 5.x Embedded started on port [8080] [INFO] Press Ctrl-C to stop the container... |
打开网络浏览器并访问http://localhost:8080/todotasks/,你会看到todotasks网络应用程序正在运行。
我们实际上并没有写太多pom.xml文件,但已经有一个建立过程带我们完成基本的生命周期。如果你查看目标目录,你还会发现一个surefire-reports目录,里面有所有运行单元测试的报告。这不是你唯一能够生成的内容,试一下Maven的site goal:
$ mvn site
这生成了一个基于项目的网站模板,存储在target/site中。如果你打开浏览器并查看那里的indext.html文件,你就会看到它。如果你见过许多Apache项目,很明显这是一个熟悉的布局。在我们的例子中,除了依赖页面外,我们没有建立许多页面,但它足以说明Maven能够帮助管理各种过程。
代码有何变化?
由于我们要建立一个纯粹的网络应用程序,代码会有相当大的变化。Swing UI类、Main.java和Controller.java文件都不复存在。现在,删除那些内容留下一个问题:Tasks对象从何而来?TasksFactory现在增加了一个方法:
private static Tasks currentTasks; public static Tasks getTasks() { if(currentTasks==null) { try { currentTasks=createTasks(TasksType.JDBM); } catch (IOException ex) { ex.printStackTrace(); } } return currentTasks; } |
它管理基于JDBM的Tasks实例的一个静态实例。任何时候一个类要引用Tasks,它会调用这个方法,并按需要建立Tasks。
从DWR出现以来,它已由milestone 2发展到milestone 3,在这个过程中,他们使JavaSrcipt代码的传送更加方便,但这样中断了我们的实例代码。
在M3中出现了一个新类ScriptBuffer,你可以把它建立在你的脚本中。ScriptBuffer有两个重要的方法:appendScript和appendData。要在缓冲器中增加一些脚本文本,你只要使用appendScript()并用一个字符串做参数即可。
sb.appendScript("remoteTaskChanged(")
要给那段脚本增加一个对象,用那个对象调用appendData():
sb.appendData(tasksevent.taskId)
当然,我们还要完善JavaScript。因此按照上面两行代码,我们需要用:
sb.appendScript(");");
关闭圆括号并结束这一行。现在,需要了解的是:ScriptBuffer方法返回ScriptBuffer本身,它允许你可以这样连接方法调用:
sb.appendScript("remoteTaskChanged(").appendData(tasksevent.taskId).
我们还修改了JavaScript客户端代码,去掉了“If something has changed, reload everything”代码并用更加智能的HTML表来代替它,清除删除行,插入需要的行,并把单独一个任务改变而引起的变化减到最少。我们还对许多你感兴趣的内容进行了改变,增加了大量注释。
进阶参阅
这里我们仅对Maven的用法进行了简单说明。我们主要利用Maven的默认行为,这些行为源自Maven的Super POM文件,所有POM文件和默认的插件都由它发展而来。因为我们只是移植一个现有的应用程序,我们不必使用Maven的原形机制——它生成新的模板式artifact目录,完善POM文件。要学习更多与Maven相关的内容,Maven网站是最权威的参考网站。Mergere的免费PDF电子书《Maven应用指南》是一本优秀的快速入门书籍。你必须注册,但值得这样做。Maven改变你开发并建立完美Java代码的方式。
DJ Walker-Morgan是一名开发者顾问,他专门研究Java和用户对用户通讯与会议。