分享ANT 中,几个实践经典案例分析
openkk
13年前
编译和部署Java应用需要使用包括特定平台的脚本、Make文件、不同的IDE以及手工操作等组成的大杂烩。现在,几乎所有的开源Java项目都在使用Ant,许多公司的开发项目也在使用Ant。Ant的大量使用,也自然带来了对总结Ant最佳实践的迫切需求。 <br /> <br /> 1. 采用一致的编码规范 <br /> Ant用户不管是喜欢还是痛恨XML构建文件的语法,都愿意跳进这一迷人的争论中。让我们先看一些保持XML构建文件简洁的方法。 <br /> 首先,也是最重要的,化费时间格式化你的XML让它看上去很清晰。不过XML是否美观,Ant都可以工作。但是丑陋的XML很难读懂。倘若你在任务之间留出空行,有规则的缩进,每行文字不超过90列,那么XML令人惊讶的易读。再加上好的编辑器或IDE高亮相应的语句,你就不会有如何阅读的麻烦。 <br /> 同样,精选有意义明确、容易读懂的词汇来命名任务和属性。比如,dir.reports就比rpts好。并不需要特定的编码规范,只要有一种规范并坚持使用就好。 <br /> <br /> 2. 将build.xml 放在项目根目录中 <br /> Ant构建文件build.xml可以放在如何位置,但是放在项目顶层目录中可以保持项目简洁。这是最普遍的规范,使开发者能够在根目录找到它。同时,也能够容易了解项目中不同目录之间的逻辑关系。以下是一个典型的项目层次: <br /> <br /> [root dir] <br /> | build.xml <br /> +--src <br /> +--lib (包含第三方 JAR包) <br /> +--build (由 build任务生成) <br /> +--dist (由 build任务生成) <br /> 当build.xml在顶级目录时,倘若你在项目某个子目录中,只要输入:ant -find compile 命令,不需要改变工作目录就能够以命令行方式编译代码。参数-find告诉Ant寻找存在于上级目录中的build.xml并执行。 <br /> <br /> 3. 使用单一构建文件 <br /> 有人喜欢将一个大项目分解到几个小的构建文件,每个构建文件分担整个构建过程的一小部分工作。但是应该认识到,将构建文件分割会增加对整个构建过程的理解难度。要注意在单一构建文件能够清楚表现构建层次的情况下,不要过工程化(over-engineer)。 <br /> 即使你把项目划分为多个构建文件,也应使程序员能够在项目根目录下找到核心build.xml。尽管该文件只是将实际构建工作委派给下级构建文件,也应保证该文件可用。 <br /> <br /> 4. 提供良好的帮助说明 <br /> 应尽量使构建文件自文档化。增加任务描述是最简单的方法。当你输入ant -projecthelp时,你就可以看到带有描述的任务清单。比如,你可以这样定义任务: <br /> <target name="compile" <br /> description="Compiles code, output goes to the build dir."> <br /> 最简单的规则是对所有你希望程序员通过命令行直接调用的任务都加上描述。对于一般用来执行中间处理过程的内部任务,比如生成代码或建立输出目录等,就无法使用描述属性。 <br /> 这时,可以通过在构建文件中加入XML注释来处理。或者专门定义一个help任务,当程序员输入ant help时来显示详细的使用说明。 <br /> <pre class="brush:xml; toolbar: true; auto-links: false;"> <target name="help" description="Display detailed usage information"> <echo>Detailed help...</echo> </target> </pre> <br /> 5. 提供清空任务 <br /> 每个构建文件都应包含一个清空任务,删除所有生成的文件和目录,使系统回到构建文件执行前的初始状态。执行清空任务后还存在的文件应处在版本控制系统的管理下。 <br /> 比如: <br /> <pre class="brush:xml; toolbar: true; auto-links: false;"> <target name="clean" description="Destroys all generated files and dirs."> <delete dir="${dir.build}"/> <delete dir="${dir.dist}"/> </target> </pre> <br /> 除非是在产生整个系统版本的特殊任务中,否则不要自动调用clean任务。当程序员仅仅执行编译任务或其他任务时,他们不需要构建文件事先执行即令人讨厌有没有必要的清空任务。要相信程序员能够确定何时需要清空所有文件。 <br /> <br /> 6. 使用ANT管理任务从属关系 <br /> 假设你的应用由Swing GUI组件、Web界面、EJB层和公共应用代码组成。在大型系统中,你需要清晰地定义Java包属于系统的哪一层。否则如何一点修改都要重新编译成千上百个文件。任务从属关系管理差会导致过度复杂而脆弱的系统。改变GUI面板的设计不应造成Servlet和EJB的重编译。 <br /> 当系统变得庞大后,稍不注意就可能将依赖于客户端的代码引入到服务端。这是因为IDE在编译文件时使用单一的classpath。Ant让你更有效地控制构建活动。 <br /> 设计你的构建文件编译大型项目的步骤:首先,编译公共应用代码,将编译结果打成JAR包文件。然后,编译上一层的项目代码,编译时依靠第一步产生的JAR文件。不断重复这一过程,直到最高层的代码编译完成。 <br /> 分步构建强化了任务从属关系管理。如果你工作在底层Java框架上,引用高层的GUI模板组件,这时代码不需要编译。这是由于构建文件在编译底层框架时,在源路径中没有包含高层GUI面板组件的代码。 <br /> <br /> 7. 定义并重用文件路径 <br /> 如果文件路径在一个地方集中定义,并在整个构建文件中得到重用,那么构建文件更易于理解。以下是这样做的一个例子: <br /> <pre class="brush:xml; toolbar: true; auto-links: false;"> <project name="sample" default="compile" basedir="."> <path id="classpath.common"> <pathelement location="${jdom.jar.withpath}"/> ...etc </path> <path id="classpath.client"> <pathelement location="${guistuff.jar.withpath}"/> <pathelement location="${another.jar.withpath}"/> <!-- reuse the common classpath --> <path refid="classpath.common"/> </path> <target name="compile.common" depends="prepare"> <javac destdir="${dir.build}" srcdir="${dir.src}"> <classpath refid="classpath.common"/> <include name="com/oreilly/common/**"/> </javac> </target> </project> </pre> <br /> 当项目不断增长,构建日益复杂时,这一技术越发体现出其价值。你可能为编译不同层次的应用定义各自的文件路径,比如运行单元测试的、运行应用程序的、运行Xdoclet的、生成JavaDocs的等等不同路径。这种组件化路径定义的方法比为每个任务单独定义路径要优越得多。否则,很容易丢失任务任务从属关系的轨迹。 <br /> <br /> 8. 定义恰当的任务参数关系 <br /> 假设dist任务从属于jar任务,那么哪个任务从属于compile任务,哪个任务从属于prepare任务呢?Ant构建文件最终定义了任务的从属关系图,它必须被仔细地定义和维护。 <br /> 应该定期检查任务的从属关系以保证构建工作得到正确执行。大的构建文件随着时间推移趋向于增加更多的任务,所以到最后由于不必要的从属关系导致构建工作非常困难。比如,你可能发现在程序员只是需要编译一些没有使用EJB的GUI代码时,重新生成EJB代码。 <br /> 以“优化”的名义忽略任务的从属关系是另一种常见的错误。这种错误迫使程序员为了得到恰当的结果必须记住并按照特定的顺序调用一串任务。更好的做法是:提供描述清晰的公共任务,这些任务包含正确的任务从属关系;另外提供一套“专家”任务让你能够手工执行个别的构建步骤,这些任务不提供完整的构建过程,但是让那些专家在快速而恼人的编码期间跳过某些步骤 <br /> <br /> 9.使用配置属性 <br /> 任何需要配置或可能发生变化的信息都应作为Ant属性定义下来。对于在构建文件中多次出现的值也同样处理。属性既可以在构建文件头部定义,也可以为了更好的灵活性而在单独的属性文件中定义。以下是在构建文件中定义属性的样式: <br /> <pre class="brush:xml; toolbar: true; auto-links: false;"> <project name="sample" default="compile" basedir="."> <property name="dir.build" value="build"/> <property name="dir.src" value="src"/> <property name="jdom.home" value="../java-tools/jdom-b8"/> <property name="jdom.jar" value="jdom.jar"/> <property name="jdom.jar.withpath" value="${jdom.home}/build/${jdom.jar}"/> etc... </project> </pre> <br /> 或者你可以使用属性文件: <br /> <pre class="brush:xml; toolbar: true; auto-links: false;"> <project name="sample" default="compile" basedir="."> <property file="sample.properties"/> etc... </project> </pre> <br /> 在属性文件 sample.properties中: <br /> <pre class="brush:java; toolbar: true; auto-links: false;"> dir.build=build dir.src=src jdom.home=../java-tools/jdom-b8 jdom.jar=jdom.jar jdom.jar.withpath=${jdom.home}/build/${jdom.jar} </pre> <br /> 用一个独立的文件定义属性是有好处的,它可以清晰地定义构建中的可配置部分。另外,在开发者工作在不同操作系统的情况下,你可以在不同的平台上提供该文件的不同版本。 <br /> <br /> 10. 保持构建过程独立 <br /> 为了最大限度的扩展性,不要应用外部路径和库文件。最重要的是不要依赖于程序员的CLASSPATH设置。取而代之的是,在构建文件中使用相对路径并定义自己的路径。如果你引用了绝对路径如C:\java\tools,其他开发者未必使用与你相同的目录结构,所以就无法使用你的构建文件 <br /> 如果你部署开发源码项目,应该提供包括所有需要的JAR文件的发行版本,当然是在遵守许可协议的基础上。对于内部项目,相关的JAR文件都应在版本控制系统的管理中,并捡出到大家都知道的位置。 <br /> 当你不得不应用外部路径时,应将路径定义为属性。使程序员能够涌适合他们自己的机器的参数重载这些属性。你也可以使用以下语法引用环 <br /> <br /> 转自: <a href="/misc/goto?guid=4959500252947275267" target="_blank">http://joecn.iteye.com/blog/1317285</a>