Java 构建工具:ant

1 ANT简介

Apache Ant,是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发。由Apache软件基金会所提供。默认情况下,它的buildfile(XML文件)名为build.xml。每一个buildfile含有一个和至少一个预设的,这些targets包含许多task elements。每一个task element有一个用来被参考的id,此id必须是唯一的。

<?xml version="1.0" ?> 
<project name="Hello World" default="execute">

	<target name="init">
		<mkdir dir="build/classes"/>
		<mkdir dir="dist"/>
	</target>
	<target name="compile" depends="init">
		<javac srcdir="src" destdir="build/classes"/>
	</target>
	
	<target name="compress" depends="compile">
	        <jar destfile="dist/HelloWorld.jar" basedir="build/classes"  />
	</target>

	<target name="execute" depends="compile">
		<java classname="HelloWorld" classpath="build/classes"/>
	</target>

</project>

以上两段内容 摘自 维基百科-Apache Ant

1.1 为什么你需要一个构建工具?

理解Apache Ant定义之前,必须了解需要一个构建工具。为什么我需要Ant,或者更具体地说,为什么我需要一个构建工具?
花你一天做以下工作?

  • 编译代码
  • 打包二进制文件
  • 部署二进制文件到测试服务器
  • 测试您的代码更改
  • 从一个位置复制代码到另一个地方

如果你回答是肯定的上述任何一项,那么现在是时候实现过程的自动化。
平均而言,开发人员花费3小时(工作日超出 8小时)做这样构建和部署平凡的任务。难道你会很高兴多要回3个小时?
Apache Ant是可以在命令行中执行一个操作系统构建和部署工具。

1.2 ANT历史

  • Ant是由 James Duncan Davidson(詹姆斯·邓肯·戴维森) 开发的(也就是 Tomcat 最初的开发者)
  • 最初是用来构建Tomcat,被作为一个Tomcat的发行版的一部分
  • Apach Make工具包的复杂性与诸多问题催生了Apache Ant
  • 在 2000年的时候,Ant被作为一个独立的项目
  • 最新的Apache Ant版本是2018年3月27日发布的 Apache Ant 1.9.11 and 1.10.3 Released

1.3 apache ant 特点

  • Ant 是最完整的基于 Java 语言开发的构建和部署工具。
  • Ant 具有平台无关性,可以处理平台特有的属性,诸如文件分隔符。
  • Ant 还可以用来执行平台特有的任务,比如使用 touch 命令修改一个文件的修改时间。
  • Ant 脚本是用 XML 来完成的。如果你已经对 XML 有所了解,那么你学习 Ant,将会比较得心应手。
  • Ant 擅长自动完成重复任务。
  • Ant 开始需要一系列的预先定义好的任务。
  • Ant 提供了开发自定义任务的接口。
  • Ant 可以很容易从命令行调用,并且它能够很好地集成免费和商用的集成开发环境。

2 环境搭建

  • apache ant 1.9.xx 需要jdk最低版本为1.7
  • apache ant 1.10.xx 需要jdk最低版本为1.8

Download JDK
Download Apache Ant 1.10.3
Download Apache Ant 1.9.11

windows

JAVA_HOME : D:\pf\java\jdk1.8.0_171
CLASSPATH : .;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\dt.jar
ANT_HOME : D:\pf\apache-ant-1.9.11
PATH : %PATH%;%JAVA_HOME%\bin;%ANT_HOME%\bin

linux
java.sh
ant.sh

tar -zxvf apache-ant-1.9.11-bin.tar.gz -C /usr/local/
vi /etc/profile.d/java.sh  
vi /etc/profile.d/ant.sh
source /etc/profile

验证是否安装成功

[root@localhost ~]# ant -version
Apache Ant(TM) version 1.9.11 compiled on March 23 2018

3 构建文件

一般来说,Ant 的构建文件默认为 build.xml,放在项目顶层目录中。然而,并没有限制构建文件必须命名为 bulid.xml,也并不限制放在项目顶层目录中。你可以将构建文件命名为其他名字,也可以将它放在项目的其他地方。

这个教程将以简单的方式向你展示如何利用 Apache Ant 来自动地构建和部署项目的过程。在完成本教程的学习以后,你将会发现你已经具备下一阶段学习 Apache Ant 中等水平的专业知识 。

对于下面的练习,创建一个文件命名为 build.xml 的文件,存储在你电脑的任意地方 ,并包含一下的内容:

<?xml version="1.0"?>
<project name="Hello World Project" default="info">

   <target name="info">
      <echo>Hello World - Welcome to Apache Ant!</echo>
   </target>

</project>

注意到上面的练习中,在 xml 文件的声明前面没有任何空行或者空格。如果你在写 xml 文件的声明时加入了空行或者空格,执行 ant -build 操作时,将会出现下面的错误信息:

The processing instruction target matching "[xX][mM][lL]" is not allowed.

错误信息的意思是:处理指令目标匹配 "[xX][mM][lL]" 不被允许。所有的构建文件需要包含项目元素 (project 标签) 和至少一个目标元素 (target 标签)。

构建文件的项目元素 有 3 个属性:

属性描述
项目名 (name)表示项目的名称。(可选)
默认 (default)表示构建脚本默认运行的目标,即制定默认的 target。一个项目 (project) 可以包含多个目标 (target)。(必须)
基准目录 (basedir)表示当该属性没有指定时,使用 Ant 的构件文件的附目录作为基准目录。(可选)

一个目标 (target) 是一系列你想运行的任务 (tasks),运行时看成一个单元。在我们的例子中,我们用一个简单的目标来为用户提供一个有信息的消息。

目标和目标之间可以有依赖关系。举个例子,一个部署 (deploy) 目标可能依赖于封装 (package) 目标,而这个封装目标可能又依赖于编译 (compile) 目标等。依赖关系被表示成依赖属性 (depends)。例如:

<target name="deploy" depends="package">
  ....
</target>

<target name="package" depends="clean,compile">
  ....
</target>

<target name="clean" >
  ....
</target>

<target name="compile" >
  ....
</target>

构建文件的目标元素有以下属性:

属性描述
目标名 (name)表示目标的名称。(必须)
依赖 (depends)用于描述 target 之间的依赖关系,若与多个 target 存在依赖关系时,需要以“,”间隔。Ant 会依照 depends 属性中 target 出现的顺序依次执行每个 target。被依赖的 target 会先执行。(可选)
描述 (description)关于 target 功能的简单描述。(可选)
如果 (if)用于验证指定的属性是否存在,若不存在,所在 target 将不会被执行。(可选)
除非 (unless)该属性的功能与 if 属性的功能正好相反,它也用于验证指定的属性是否存在,若不存在,所在 target 将会被执行。(可选)

在上面的例子中 echo 任务主要负责打印消息。在我们的例子中,执行 echo 任务后,打印出 “hello world” 消息。

为了运行 ant 的构建文件,打开命令提示符并导航到 build.xml 建立的文件夹。输入 ant info 命令或者 ant 命令。这两种命令都可以运行,因为 info 是构建文件的默认目标。你讲会看到下面的输出信息:

[root@localhost www]# ant
Buildfile: /opt/www/build.xml

info:
     [echo] Hello World - Welcome to Apache Ant!

BUILD SUCCESSFUL
Total time: 0 seconds
[root@localhost www]#

4 属性任务

Ant 构建文件是用 XML 编写的,它不能像你喜欢的编程语言那样去声明变量。然而,正如你可能已经想到的,如果允许 Ant 声明变量,如项目名称,项目源目录等,这将是非常有用的。

Ant 使用属性 (property) 元素来让你能够具体说明属性。这就允许这些属性能够在不同的构建和不同的环境下发生改变。

默认情况下,Ant 提供以下预定义的属性,这些属性都是可以在构建文件中使用的:

属性解释
ant.file该构建文件的完整地址
ant.version安装的 Apache Ant 的版本
basedir构建文件的基目录的绝对路径,作为 project 元素的 basedir 属性
ant.java.versionAnt 使用的 JAVA 语言的软件开发工具包的版本
ant.project.name项目的名字,具体声明为 project 元素的 name 属性
ant.project.default-target当前项目的默认目标
ant.project.invoked-targets在当前项目中被调用的目标的逗号分隔列表
ant.core.libAnt 的 jar 文件的完整的地址
ant.homeAnt 安装的主目录
ant.library.dirAnt 库文件的主目录,特别是 ANT_HOME/lib 文件夹

Ant 也确保系统属性在构建文件中可用,如 file.separator。

除了上述内容以外,用户也可以使用 property 元素定义一些额外的属性。下面的例子就演示了怎样去定义一个叫做 sitename 的属性:

<?xml version="1.0"?>
<project name="Hello World Project" default="info">

   <property name="sitename" value="www.xyzla.com.com"/>
   <target name="info">
      <echo>Apache Ant version is ${ant.version} - You are at ${sitename} </echo>
   </target>

</project>

在上述的构建文件下运行 Ant 可以产生以下输出:

[root@localhost www]# ant
Buildfile: /opt/www/build.xml

info:
     [echo] Apache Ant version is Apache Ant(TM) version 1.9.11 compiled on March 23 2018 - You are at www.xyzla.com 

BUILD SUCCESSFUL
Total time: 0 seconds
[root@localhost www]#

5 属性文件

当你只需要对小部分属性进行设置时,可以选择直接在构建文件中设置。然而,对于大项目,最好将设置属性的信息存储在一个独立的文件中。

存储属性信息在一个独立的文件中将会提供以下好处:

它可以让您重复使用相同的构建文件,该文件在不同的执行环境中使用不同的属性设置。例如,构建属性文件在 DEV , TEST , 和 PROD 环境中可以独立地被维护。
当你事先不知道属性的值时(例如,在一个实际的环境中),这样处理是有益的。这样允许你在知道属性值后,在其他环境中执行生成 (build) 操作。
这里没有硬性规定,但是一般情况下,属性文件都被命名为 build.properties, 并且与 build.xml 存放在同一目录层。 你可以基于部署环境 ——比如: build.properties.dev 和 build.properties.test 创建多个 build.properties 文件。

在下面的例子中展示了 build.xml 文件和与之相联系的 build.properties文件:

build.xml

<?xml version="1.0"?>
<project name="Hello World Project" default="info">

   <property file="build.properties"/>

   <target name="info">
      <echo>Apache Ant version is ${ant.version} - You are at ${sitename} </echo>
   </target>

</project>

build.properties

sitename=wiki.xyzla.com
buildversion=3.3.2

注意到上面的练习中,sitename 是一个自定义属性,执行后映射到一个地址为 “wiki.jikexueyuan.com” 的网站上。你可以用这种方式声明任意数量的属性。在上面的例子中,还有一个自定义属性 buildversioin,它表明了当前构建的版本号。

除了以上提到的两个属性, Ant 还提供了其他内置属性,在前一章节中已经提到,但是下面我们再一次给出相关属性。

属性描述
ant.file表示 buildfile 的绝对路径。
ant.version表示 Ant 的版本。
basedir表示 project 基目录的绝对路径。
ant.jave.version表示 Ant 检测到的 JDK 的版本。
ant.project.name表示当前指定的 project 的名字。
ant.project.default-target表示当前项目的默认目标。
ant.project.invoked-targets表示被当前项目调用的一系列用逗号分隔开的目标。
ant.core.lib表示 Ant jar 文件的绝对路径。
ant.home表示 Ant 安装的根目录。
ant.library.dir表示 Ant 函数库,一般情况下为 ANT_HOME/lib 文件的根目录。

在这一节的例子中,我们用到的 Ant 内置属性是 ant.version 属性。

6 数据类型

Ant 提供一些预定义的数据类型。不要将术语“数据类型”和那些在编程语言中可用的数据类型相混淆,而是将他们视作一组已经在产品中配置好的服务。

下述的数据类型是由 Apache Ant 提供的。

文件集

文件集的数据类型代表了一个文件集合。它被当作一个过滤器,用来包括或移除匹配某种模式的文件。

例如,参考下面的代码。这里,src 属性指向项目的源文件夹。

文件集选择源文件夹中所有的 .java 文件,除了那些包含有 'Stub' 单词的文件。能区分大小写的过滤器被应用到文件集上,这意味着名为 Samplestub.java 的文件将不会被排除在文件集之外。

<fileset dir="${src}" casesensitive="yes">
   <include name="**/*.java"/>
   <exclude name="**/*Stub*"/>
</fileset>

模式集合

一个模式集合指的是一种模式,基于这种模式,能够很容易地过滤文件或者文件夹。模式可以使用下述的元字符进行创建。

  • ? -仅匹配一个字符
  • * -匹配零个或者多个字符
  • ** -递归地匹配零个或者多个目录

下面的例子演示了模式集合的使用。

<patternset id="java.files.without.stubs">
   <include name="src/**/*.java"/>
   <exclude name="src/**/*Stub*"/>
</patternset>

该模式集合能够通过一个类似于下述的文件集进行重用:

<filelist id="config.files" dir="${webapp.src.folder}">
   <file name="applicationConfig.xml"/>
   <file name="faces-config.xml"/>
   <file name="web.xml"/>
   <file name="portlet.xml"/>
</filelist>

文件列表

文件列表数据类型与文件集相类似,除了以下几处不同:

  • 文件列表包含明确命名的文件的列表,同时其不支持通配符。
  • 文件列表数据类型能够被应用于现有的或者还不存在的文件中。

让我们来看一个下述的关于文件列表数据类型的例子。在这个例子中,属性 webapp.src.folder 指向该项目中的 Web 应用的源文件夹。

<fileset dir="${src}" casesensitive="yes">
   <patternset refid="java.files.without.stubs"/>
</fileset>

过滤器集合

使用一个过滤器集合数据类型与拷贝任务,你可以在所有文件中使用一个替换值来替换掉一些与模式相匹配的文本。

一个常见的例子就是对一个已经发行的说明文件追加版本号,代码如下:

<copy todir="${output.dir}">
   <fileset dir="${releasenotes.dir}" includes="**/*.txt"/>
   <filterset>
      <filter token="VERSION" value="${current.version}"/>
   </filterset>
</copy>

在这段代码中:

  • 属性 output.dir 指向项目的输出文件夹。
  • 属性 releasenotes.dir 指向项目的发行说明文件夹。
  • 属性 current.version 指向项目的当前版本文件夹。
  • 拷贝任务,顾名思义,是用来将文件从一个地址拷贝到另一个地址。

路径

path 数据类型通常被用来表示一个类路径。各个路径之间用分号或者冒号隔开。然而,这些字符在运行时被替代为执行系统的路径分隔符。

类路径被设置为项目中 jar 文件和类文件的列表,如下面例子所示:

<path id="build.classpath.jar">
   <pathelement path="${env.J2EE_HOME}/${j2ee.jar}"/>
   <fileset dir="lib">
      <include name="**/*.jar"/>
   </fileset>
</path>

在这段代码中:

  • 属性 env.J2EE_HOME 指向环境变量 J2EE_HOME 。
  • 属性 j2ee.jar 指向在 J2EE 基础文件夹下面的名为 J2EE jar 的文件。

7 构建项目

现在我们已经学习了 Ant 的数据类型,是时候在实际过程中运用所学知识了。在这一节中,我们将会构建一个项目。这一章节的目的是创建一个 Ant build 文件,该文件能够编译 Java 源文件和将这些类文件存储在 WEB-INF\classes 文件夹下。

考虑接下来构建项目的结构:

  • 数据脚本存储在 db 文件夹中。
  • java 源文件存储在 src 文件夹中。
  • images (图像),js (JavaScript 脚本),style (css 层叠样式表)存储在 war 文件夹中。
  • JSPs 文件存储在 jsp 文件夹中。
  • 第三方的 jar 文件存储在 lib 文件夹中。
  • java 类文件存储在 WEB-INF\classes 文件夹中。

学习完本教程的剩余部分后,就能知道这个项目是一个 Hello World 传真应用。

C:\work\FaxWebApplication>tree
Folder PATH listing
Volume serial number is 00740061 EC1C:ADB1
C:.
+---db
+---src
.  +---faxapp
.  +---dao
.  +---entity
.  +---util
.  +---web
+---war
   +---images
   +---js
   +---META-INF
   +---styles
   +---WEB-INF
      +---classes
      +---jsp
      +---lib

下面给出上述项目的 build.xml 文件的内容。让我们来一条语句接一条语句地来分析它。

<?xml version="1.0"?>
<project name="fax" basedir="." default="build">
   <property name="src.dir" value="src"/>
   <property name="web.dir" value="war"/>
   <property name="build.dir" value="${web.dir}/WEB-INF/classes"/>
   <property name="name" value="fax"/>

   <path id="master-classpath">
      <fileset dir="${web.dir}/WEB-INF/lib">
         <include name="*.jar"/>
      </fileset>
      <pathelement path="${build.dir}"/>
   </path>

   <target name="build" description="Compile source tree java files">
      <mkdir dir="${build.dir}"/>
      <javac destdir="${build.dir}" source="1.5" target="1.5">
         <src path="${src.dir}"/>
         <classpath refid="master-classpath"/>
      </javac>
   </target>

   <target name="clean" description="Clean output directories">
      <delete>
         <fileset dir="${build.dir}">
            <include name="**/*.class"/>
         </fileset>
      </delete>
   </target>
</project>

首先,让我们来声明一些源文件,web 文件和构建文件的一些属性信息。

<property name="src.dir" value="src"/>
<property name="web.dir" value="war"/>
<property name="build.dir" value="${web.dir}/WEB-INF/classes"/>

在上面的例子中:

  • src.dir 表示这个项目的源文件目录,也就是存储 java 文件的地方。
  • web.dir 表示这个项目的 web 文件目录,也就是存储 JSPs 文件,web.xml,css,javascript 以及其它与 web 相关的文件的地方。
  • build.dir 表示该项目的输出文件。

属性也可以引用其它属性。在上面的例子中,build.dir 属性引用了 web.dir 属性。

在上面的例子中,src.dir 就是项目源文件存放的地方。

我们项目的默认目标是编译目标。但是首先让我们来看一下 clean 目标。

clean 目标,就像它的名字所表明的意思一样,删除构建文件夹中的所有文件。

<target name="clean" description="Clean output directories">
   <delete>
      <fileset dir="${build.dir}">
         <include name="**/*.class"/>
      </fileset>
   </delete>
</target>

控制类路径 (master-classpath) 保存类路径的相关信息。在这种情况下,它包含了构建文件夹和 jar 文件夹中的所有的类文件。

<path id="master-classpath">
   <fileset dir="${web.dir}/WEB-INF/lib">
      <include name="*.jar"/>
   </fileset>
   <pathelement path="${build.dir}"/>
</path>

最后,构建目标构建这些文件。首先,我们创建一个构建目录,如果该目录不存在,我们就执行 javac 命令(具体以 jdk 1.5 作为我们目标的编译环境)。 我们对 javac 任务提供源文件夹和类路径,并且通过执行 javac 任务将类文件存放在构建文件夹中。

<target name="build" description="Compile main source tree java files">
   <mkdir dir="${build.dir}"/>
   <javac destdir="${build.dir}" source="1.5" target="1.5" debug="true" deprecation="false" optimize="false" failonerror="true">
      <src path="${src.dir}"/>
      <classpath refid="master-classpath"/>
   </javac>
</target>

在这个文件上执行 Ant,编译 java 源文件,并将编译后的类文件存放在构建文件夹的地方。

运行 Ant 文件后,能看到以下输出:

C:\>ant
Buildfile: C:\build.xml

BUILD SUCCESSFUL
Total time: 6.3 seconds

文件被编译后,将存储在 build.dir 文件夹中。

8 生成文档

文档在任何项目中都是必须的。文档对一个项目的维护起了至关重要的作用。 通过使用内置的 Javadoc 工具,使用 Java 生成文档变得更加容易。Ant 通过按需生成文档使得这个步骤甚至变得更简单。

如你所知,javadoc 工具具有高度的灵活性,而且其还允许进行一些配置。Ant 通过使用 javadoc 任务的方式来公开这些配置选项。如果你对 javadoc 不熟悉的话,我们建议你先看一下 Java 文档教程。

下述的章节列出了在 Ant 中最常使用的 javadoc 的选项。

属性

源包括源路径,源路径引用或者源文件三个属性。

源路径 (sourcepath) 指向源文件所在的文件夹,例如: src 文件夹。
源路径引用 (sourcepathref) 指向由该路径属性引用的路径,例如:delegates.src.dir 。
源文件 (sourcefiles) 在你想指定单独的文件时使用,比如指定一个逗号分隔列表。
目标路径是通过使用 destdir 文件夹来指定的,例如 build.dir 。

你能够通过指定应被包括的包的名字来过滤 javadoc 任务。这可以通过使用 packagenames 属性实现,即一个以逗号分隔的包文件列表。

你可以过滤 javadoc 过程以只显示公有的,私有的,包或者被保护的类和成员。这些可以通过使用 private,public,package 和 protected 属性实现。

你也可以通过使用相应的属性来告诉 javadoc 任务去包含作者和版本信息。

你也可以使用 group 属性将所有的包组织在一起,以使得他们更易被操作。

将上述内容集中到一起

让我们继续我们的主题,Hello world 传真应用程序。让我们给我们的传真应用项目添加一个文档目标。

下面给出的例子是我们在项目中使用的 javadoc 任务。在这个例子中,我们指定 javadoc 去使用 src.dir 作为源目录,doc 作为目标。

我们还定制窗口标题,标题,以及显示在 java 文档页上的页脚信息。

此外,我们还创建了三个组:

为源文件夹中的实用工具类创建了一个组。
为用户接口的类创建了一个组。
为数据库相关的类创建了一个组。
您可能会注意到,数据包组含有两个包 -- faxapp.entity 和 faxapp.dao 。

<target name = "generate-javadoc">
  <javadoc packagenames="faxapp.*" sourcepath="${src.dir}" 
     destdir = "doc" version = "true" windowtitle = "Fax Application">

     <doctitle><![CDATA[= Fax Application =]]></doctitle>

     <bottom>
        <![CDATA[Copyright © 2011. All Rights Reserved.]]>
     </bottom>

     <group title = "util packages" packages = "faxapp.util.*"/>
     <group title = "web packages" packages = "faxapp.web.*"/>
     <group title = "data packages" packages = "faxapp.entity.*:faxapp.dao.*"/>
  </javadoc>

  <echo message = "java doc has been generated!" />
</target>

让我们运行 javadoc Ant 任务。它将生成 java 文档文件,并将这些文件放置于 doc 文件夹中。

当执行 javadoc 目标时,其产生以下的输出:

>C:\>ant generate-javadoc
>Buildfile: C:\build.xml

>java doc has been generated!

>BUILD SUCCESSFUL
>Total time: 10.63 second

java 文档文件现在出现在 doc 文件夹中。

通常情况下,javadoc 文件作为发行版或者包目标的一部分。

9 生成 JAR 文件

编译完你的 java 源文件后,接下来就构建 java 存档,例如: JAR 文件。创建 Ant 中的 JAR 文件十分简单,运用 jar 任务来生成 jar 包。在 jar 任务中常用的属性如下所示:

属性描述
basedir表示输出 JAR 文件的基目录。默认情况下,为项目的基目录。
compress表示告知 Ant 对于创建的 JAR 文件进行压缩。
keepcompression表示 project 基目录的绝对路径。
destfile表示输出 JAR 文件的名字。
duplicate表示发现重复文件时 Ant 执行的操作。可以是添加、保存、或者是使该重复文件失效。
excludes表示移除的文件列表,列表中使用逗号分隔多个文件。
excludesfile与上同,但是使用模式匹配的方式排除文件。
inlcudes与 excludes 正好相反。
includesfile表示在被归档的文件模式下,打包文件中已有的文件。与 excludesfile 相反。
update表示告知 Ant 重写已经建立的 JAR 文件。

继续我们的 Hello World 传真应用项目,通过添加一个新的目标 target 来产生 jar 文件。 但是在此之前,让我们先来考虑下面给出的 jar 任务。

<jar destfile = "${web.dir}/lib/util.jar"
   basedir = "${build.dir}/classes"
   includes = "faxapp/util/**"
   excludes = "**/Test.class" />

这里,web.dir 属性指出了 web 源文件的路径。在我们的案例中, web 源文件路径也就是存放 util.jar 的地方。

在我们的案例中,build.dir 属性指出了配置文件夹的存储路径,也就是存放 util.jar 类文件的地方。

在上面的代码中,我们利用来自 faxapp.util 包中的类文件创建了一个名为 util.jar 的 jar 包。然而,我们排除名字为 Test 的类文件。输出的 jar 文件将会存放在 web 应用的配置文件 lib 中。

如果我们想 util.jar 成为可执行文件,只需在 Main-Class 元属性中加入manifest.

这样,上面给出的代码,在加入 Main-Class 元属性后,可以更新为如下形式:

<jar destfile = "${web.dir}/lib/util.jar"
   basedir = "${build.dir}/classes"
   includes = "faxapp/util/**"
   excludes = "**/Test.class">
   <manifest>
      <attribute name = "Main-Class" value = "com.tutorialspoint.util.FaxUtil"/>
   </manifest>
</jar>

为了执行 jar 任务,将它包装在目标 target 中,最常见的情况是,将 jar 任务包装在配置目标或者打包目标中(build 目标或 package 目标),并执行包装后的目标。

<target name="build-jar">
   <jar destfile="${web.dir}/lib/util.jar"
      basedir="${build.dir}/classes"
      includes="faxapp/util/**"
      excludes="**/Test.class">

      <manifest>
         <attribute name="Main-Class" value="com.tutorialspoint.util.FaxUtil"/>
      </manifest>

   </jar>
</target>

在上述文件上运行 Ant ,就能创建出 util.jar。

上述文件运行 Ant 后,得到以下的输出:

C:\>ant build-jar
Buildfile: C:\build.xml

BUILD SUCCESSFUL
Total time: 1.3 seconds

最后得到的输出 util.jar 将被存储在输出文件夹中。

10 生成 WAR 文件

使用 Ant 创建 WAR 文件是极其简单的。这与创建 JAR 文件任务非常类似。 毕竟,WAR 文件与 JAR 文件只是两种不同的 ZIP 文件。

WAR 任务是 JAR 任务的一个扩展,但是其对控制哪些文件进入 WEB-INF/classes 文件夹和生成 web.xml 文件进行了一些很好的补充。WAR 任务对指定 WAR 文件布局是非常有用的。

既然 WAR 任务是 JAR 任务的一个扩展,JAR 任务的所有的属性都适用于 WAR 任务。

属性描述
webxmlweb.xml 文件的路径
lib指定什么文件可以进入 WEB-INF\lib 文件夹的一个组
classes指定什么文件可以进入 WEB-INF\classes 文件夹的一个组
metainf指定生成 MANIFEST.MF 文件的指令

继续我们的 Hello World 传真应用项目,让我们添加一个新的目标来生成 jar 文件。 但是在此之前,我们需要考虑一下 war 任务。 请看下面的例子:

<war destfile = "fax.war" webxml = "${web.dir}/web.xml">
   <fileset dir = "${web.dir}/WebContent">
      <include name = "**/*.*"/>
   </fileset>
   <lib dir = "thirdpartyjars">
      <exclude name = "portlet.jar"/>
   </lib>
   <classes dir = "${build.dir}/web"/>
</war>

按照前面的例子中,web.dir 变量指向源 web 文件夹,即该文件包含 JSP,css 和 javascript 文件等等。

该 build.dir 变量指向输出文件夹,WAR 的包能在该文件夹下找到。 通常情况下, 类将被绑定到 WAR 文件下的 WEB-INF/classes 文件夹下。

在这个例子中,我们创建了一个名为 fax.war 的 war 文件。WEB.XML 文件可以从 web 源文件中获取。 所有 web 下来自 “WebContent” 的文件都被复制到 WAR 文件中。

WEB-INF/lib 文件夹中存储了来自于第三方 jar 文件夹中的 jar 文件。但是,我们排除了 portlet.jar,因为该 jar 文件已经存在于应用服务器的 lib 文件夹中了。最后,我们从一个构建目录下的 web 文件夹中复制所有的类,并将复制的类全部放入 WEB-INF/classes 文件夹下。

将一个 war 任务封装到一个 Ant 任务中并运行它。 这将在指定位置创建一个 WAR 文件。

类,库,metainf 和 webinf 目录完全可以进行嵌套以使得他们都能存在于项目结构下分散的文件夹中。 但是最佳的实践建议是,你的 web 项目的 web 内容架构应该与 WAR 文件类似。 传真应用项目的架构就是使用了这个基本原理。

要执行 war 任务,将其封装在一个目标里面,最常见的是,构建目标或者是包目标,然后运行它们。

<target name="build-war">

   <war destfile="fax.war" webxml="${web.dir}/web.xml">
      <fileset dir="${web.dir}/WebContent">
         <include name="**/*.*"/>
      </fileset>

      <lib dir="thirdpartyjars">
         <exclude name="portlet.jar"/>
      </lib>

      <classes dir="${build.dir}/web"/>
   </war>

</target>

在这个文件上运行 Ant 会替我们创建 fax.war 文件。

下述的输出就是运行 Ant 文件的结果:

>C:\>ant build-war
>Buildfile: C:\build.xml

>BUILD SUCCESSFUL
>Total time: 12.3 seconds

该 fax.war 文件当前被放置在输出文件夹中。war 文件的内容如下所示:

>fax.war:
   >+---jsp :这个文件夹包含了 jsp 文件
   >+---css :这个文件夹包含了 stylesheet 文件
   >+---js :这个文件夹包含了  javascript 文件
   >+---images:这个文件夹包含了  image 文件
   >+---META-INF:这个文件夹包含了  Manifest.Mf
   >+---WEB-INF
           >+---classes :这个文件夹包含了编译好的类
          >+---lib :第三方库和使用程序 jar 文件
          >WEB.xml :定义 WAR 包的配置文件 

11 封装应用

我们通过 Hello World Fax Web 应用,已经琐碎地学习了 Ant 的不同方面的知识了。

现在是时候把我们所学的知识都运用起来创建一个全面和完整的 build.xml 文件了。考虑下面给出的 build.properties 和 build.xml 文件:

build.properties

eploy.path = c:\tomcat6\webapps

build.xml

<?xml version = "1.0"?>

<project name = "fax" basedir = "." default = "usage">

   <property file = "build.properties"/>
   <property name = "src.dir" value = "src"/>
   <property name = "web.dir" value = "war"/>
   <property name = "javadoc.dir" value = "doc"/>
   <property name = "build.dir" value = "${web.dir}/WEB-INF/classes"/>
   <property name = "name" value = "fax"/>

   <path id = "master-classpath">
      <fileset dir = "${web.dir}/WEB-INF/lib">
         <include name = "*.jar"/>
      </fileset>
      <pathelement path = "${build.dir}"/>
   </path>

   <target name = "javadoc">
      <javadoc packagenames = "faxapp.*" sourcepath = "${src.dir}" 
         destdir = "doc" version = "true" windowtitle = "Fax Application">

         <doctitle><![CDATA[<h1> =  Fax Application  = </h1>]]>
         </doctitle>

         <bottom><![CDATA[Copyright ? 2011. All Rights Reserved.]]>
         </bottom>

         <group title = "util packages" packages = "faxapp.util.*"/>
         <group title = "web packages" packages = "faxapp.web.*"/> 
         <group title = "data packages" packages = "faxapp.entity.*:faxapp.dao.*"/>
      </javadoc>
   </target>

   <target name = "usage">
      <echo message = ""/>
      <echo message = "${name} build file"/>
      <echo message = "-----------------------------------"/>
      <echo message = ""/>
      <echo message = "Available targets are:"/>
      <echo message = ""/>
      <echo message = "deploy    --> Deploy application as directory"/>
      <echo message = "deploywar --> Deploy application as a WAR file"/>
      <echo message = ""/>
   </target>

   <target name = "build" description = "Compile main source tree java files">
      <mkdir dir = "${build.dir}"/>

      <javac destdir = "${build.dir}" source = "1.5" target = "1.5" debug = "true"
         deprecation = "false" optimize = "false" failonerror = "true">

         <src path = "${src.dir}"/>
         <classpath refid = "master-classpath"/>

      </javac>
   </target>

   <target name = "deploy" depends = "build" description = "Deploy application">
      <copy todir = "${deploy.path}/${${name}" preservelastmodified = "true">

         <fileset dir = "${web.dir}">
            <include name = "**/*.*"/>
         </fileset>

      </copy>
   </target>

   <target name = "deploywar" depends = "build" description = "Deploy application as a WAR file">

      <war destfile = "${name}.war" webxml = "${${web.dir}/WEB-INF/web.xml">
         <fileset dir = "${web.dir}">
            <include name = "**/*.*"/>
         </fileset>
      </war>

      <copy todir = "${deploy.path}" preservelastmodified = "true">
         <fileset dir = ".">
            <include name = "*.war"/>
         </fileset>
      </copy>

   </target>

   <target name = "clean" description = "Clean output directories">
      <delete>
         <fileset dir = "${build.dir}">
            <include name = "**/*.class"/>
         </fileset>
      </delete>
   </target>

</project>

在上面给出的例子中:

  • 我们首先在 build.properties 文件中声明了存放 Tomcat 的 webapp 文件夹的路径,并用变量 deploy.path 来保存。
  • 我们声明一个源文件夹来存放 java 文件,并用变量 src.dir 来保存。
  • 接下来,我们声明另一个源文夹来存放 web 文件,并用变量 web.dir 来保存。变量 javadoc.dir 用来存储 java 文档,变量 build.dir 是用来存储配置输出文件的路径。
  • 然后,我们给这个 web 应用命名,也就是 fax 传真。
  • 我们还定义了包含 JAR 文件的基本类路径,在上面给出的项目中也就是: WEB-INF/lib 文件夹。
  • 我们还将 build.dir 中的类文件存放在基本类路径下。
  • 这个 Javadoc 目标产生项目所需的文档,以及说明目标使用的 javadoc 文档。

上述的例子向我们展示了两个部署目标: deploy 和 deploywar 。

这个 deploy 目标将文件从 web 目录复制到部署目录,并保存最后修改日期时间戳。这样很有用,特别是当我们将项目部署到服务器上,并且该服务器支持热部署。(释义:所谓热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。)

这个 clean 目标清楚所有之前的构建文件。

这个 deploywar 目标构建 war 文件,然后将 war 文件复制到应用程序服务器的部署目录。

  • qq_43638135
    妲己再美究为妃: 博主没有想过自己接一些私活干吗?我现在还没毕业,但是我也确实听说外挂市场自动化游戏脚本市场挺火热的,并且报酬也很丰厚,但是具体的我也不是很清楚,求解答。 (1个月前 #47楼) 查看回复(2) 举报 回复
    22