ANT资料翻译-home/manual/developing_with_ant/tutorials/Hello World

Tutorial: Hello World with Apache Ant

hello world 案例

This document provides a step by step tutorial for starting java programming with Apache Ant. It does not contain deeper knowledge about Java or Ant. This tutorial has the goal to let you see, how to do the easiest steps in Ant.

这篇文章介绍了如何使用ANT一步一步构建简单的JAVA项目,所涉及的知识不太深。

Content

Preparing the project

准备工程

We want to separate the source from the generated files, so our java source files will be in src folder. All generated files should be under build, and there splitted into several subdirectories for the individual steps: classes for our compiled files and jar for our own JAR-file.

为了能够把源代码从编译后的文件中分开出来,我们会创建一个src文件夹用于保存java源代码。所有新生成的文件都要放到build文件夹下,然后按照文件类型的不同放入放入不同的子文件夹中,比如class文件夹存放编译后文件,jar文件夹存放JAR包。


We have to create only the src directory. (Because I am working on Windows, here is the win-syntax - translate to your shell):

我必须创建名为src的文件夹。(因为我在windows上开发。下面是cmd指令)

md src


The following simple Java class just prints a fixed message out to STDOUT, so just write this code intosrc\oata\HelloWorld.java.

下面是一个HelloWorld的java代码,文件路径是src\oata\HelloWorld.java。


package oata;

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}


Now just try to compile and run that:

好了,现在我们开始编译一下然后运行它。

md build\classes // 创建文件目录
javac -sourcepath src -d build\classes src\oata\HelloWorld.java //编译文件,其中指明了文件目录和编译后目录
java -cp build\classes oata.HelloWorld // 运行文件,其中指明了文件路径
 

which will result in

结果如下显示。

Hello World

Creating a jar-file is not very difficult. But creating a startable jar-file needs more steps: create a manifest-file containing the start class, creating the target directory and archiving the files.

虽然创建一个Jar文件很简单,但是要创建一个可以开始运行的jar文件需要很多步骤:我们需要创建一个名为manifest-file的文件,它包含开始类的信息,还得创建目标文件夹以及整理许多文件。

echo Main-Class: oata.HelloWorld>myManifest // 创建文件myManifest,并写入数据 Main-Class: oata.HelloWorld
md build\jar  // 创建文件夹
jar cfm build\jar\HelloWorld.jar myManifest -C build\classes .// 打包,其中指明了生成后的jar包名,需要放入的文件,以及待打包的文件路径
java -jar build\jar\HelloWorld.jar

Note: Do not have blanks around the >-sign in the echo Main-Class instruction because it would falsify it!

在>的左右不要有空格号,因为程序会做逻辑。

Four steps to a running application

四步运行案例

After finishing the java-only step we have to think about our build process. We have to compile our code, otherwise we couldn't start the program. Oh - "start" - yes, we could provide a target for that. We should package our application. Now it's only one class - but if you want to provide a download, no one would download several hundreds files ... (think about a complex Swing GUI - so let us create a jar file. A startable jar file would be nice ... And it's a good practise to have a "clean" target, which deletes all the generated stuff. Many failures could be solved just by a "clean build".

刚才我们使用了JAVA的方式运行了一个helloworld程序,现在我们需要考虑刚才的构建过程。首先我们需要编译代码,要不然就无法运行程序。我们可以把它叫做“start”。我们还需要打包我们的工程。刚才只有一个文件,但是当你需要提供一个多文件下载的时候,你不可能让别人去下载几百个文件吧...。(所以我们需要创建打包的jar文件。一个可以开始的jar文件一定会很棒...并且会有一个“clean”功能,它可以删除所有生成的东西。你可以创建一个“clean build”目标就可以通过删除build文件夹下的内容而使构建失效。

作者补充:ANT中的target(目标/任务)可以理解为一个功能,它可以很多个子目标。


By default Ant uses build.xml as the name for a buildfile, so our .\build.xml would be:

// ant的默认构建文件时build.xml

<project>
    // 清除
    <target name="clean">
        <delete dir="build"/>
    </target>
    // 编译
    <target name="compile">
        <mkdir dir="build/classes"/>
        <javac srcdir="src" destdir="build/classes"/>
    </target>
    // 打包
    <target name="jar">
        <mkdir dir="build/jar"/>
        <jar destfile="build/jar/HelloWorld.jar" basedir="build/classes">
            <manifest>
                <attribute name="Main-Class" value="oata.HelloWorld"/>
            </manifest>
        </jar>
    </target>
    // 运行
    <target name="run">
        <java jar="build/jar/HelloWorld.jar" fork="true"/>
    </target>

</project>

Now you can compile, package and run the application via

编译,打包,运行

ant compile
ant jar
ant run

Or shorter with 

也可以这样

ant compile jar run

While having a look at the buildfile, we will see some similar steps between Ant and the java-only commands:

我们可以比较下java版本和ant版本

java-onlyAnt
md build\classes
javac
    -sourcepath src
    -d build\classes
    src\oata\HelloWorld.java
echo Main-Class: oata.HelloWorld>mf
md build\jar
jar cfm
    build\jar\HelloWorld.jar
    mf
    -C build\classes
    .



java -jar build\jar\HelloWorld.jar
  
<mkdir dir="build/classes"/>
<javac
    srcdir="src"
    destdir="build/classes"/>
<!-- automatically detected -->
<!-- obsolete; done via manifest tag -->
<mkdir dir="build/jar"/>
<jar
    destfile="build/jar/HelloWorld.jar"

    basedir="build/classes">
    <manifest>
        <attribute name="Main-Class" value="oata.HelloWorld"/>
    </manifest>
</jar>
<java jar="build/jar/HelloWorld.jar" fork="true"/>
  

Enhance the build file

上面的那个build.xml太简单了,我们写个更好的。

Now we have a working buildfile we could do some enhancements: many time you are referencing the same directories, main-class and jar-name are hard coded, and while invocation you have to remember the right order of build steps.

现在我们要重新写下build.xml文件了,上面那个版本有很多缺陷,比如硬编码。硬编码很不好,你懂得。

The first and second point would be addressed with properties, the third with a special property - an attribute of the <project>-tag and the fourth problem can be solved using dependencies.

对于第一、二点,我们可以使用properties属性,第三点使用<project>标签的特别属性,至于第四点可以使用注入。

作者补充:貌似我从原文中只看到了三点,怎么看都没四点。不过不管他了,这两段的意思总的说就是上个版本有好多缺陷,我们在下面将会优化它。

<project name="HelloWorld" basedir="." default="main"> // 工程根目录

    <property name="src.dir"     value="src"/> // 变量

    <property name="build.dir"   value="build"/>
    <property name="classes.dir" value="${build.dir}/classes"/> // 美元字符内为引用变量
    <property name="jar.dir"     value="${build.dir}/jar"/>

    <property name="main-class"  value="oata.HelloWorld"/>



    <target name="clean"> 
        <delete dir="${build.dir}"/>
    </target>

    <target name="compile">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
    </target>

    <target name="jar" depends="compile"> // depends可以理解为依赖,就是说运行这个target会首先运行依赖的target,如果依赖的target也有依赖,就会出现递归。
        <mkdir dir="${jar.dir}"/>
        <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
            <manifest>
                <attribute name="Main-Class" value="${main-class}"/>
            </manifest>
        </jar>
    </target>

    <target name="run" depends="jar">
        <java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
    </target>

    <target name="clean-build" depends="clean,jar"/>

    <target name="main" depends="clean,run"/>

</project>
作者心得:build.xml是一个配置文件,实现这个逻辑的当然还是java代码,因为ant是java写的。所以出现“depends”等等,可以往java方面联想。

Now it's easier, just do a ant and you will get

// 运行 ant main

作者心得:从运行显示的顺序就可以看出depends属性值所表示的意思了。

Buildfile: build.xml

clean:

compile:
    [mkdir] Created dir: C:\...\build\classes
    [javac] Compiling 1 source file to C:\...\build\classes

jar:
    [mkdir] Created dir: C:\...\build\jar
      [jar] Building jar: C:\...\build\jar\HelloWorld.jar

run:
     [java] Hello World

main:

BUILD SUCCESSFUL

Using external libraries

使用扩展包

Somehow told us not to use syso-statements. For log-Statements we should use a Logging-API - customizable on a high degree (including switching off during usual life (= not development) execution). We use Log4J for that, because

很多时候我们不会使用原生态的语句。比如我们会使用日志外包的API去写日志,毕竟这些外包对特定的事情都很专业嘛。我们这里使用LOG4J,因为

  • it is not part of the JDK (1.4+) and we want to show how to use external libs
  • 它不是JDK的一部分并且我们想展示如何使用外包。
  • it can run under JDK 1.2 (as Ant)
  • 它可以运行在JDK1.2版本下
  • it's highly configurable
  • 它高度可配。
  • it's from Apache ;-)
  • 呵呵,它源自我们阿帕奇。

We store our external libraries in a new directory lib. Log4J can be downloaded [1] from Logging's Homepage. Create the lib directory and extract the log4j-1.2.9.jar into that lib-directory. After that we have to modify our java source to use that library and our buildfile so that this library could be accessed during compilation and run.

我们使用新的文件夹存放外包。你可以从官网中下载LOG4J。创建lib文件夹,并解压下载来的log4j-1.2.9.jar包。现在为了能够使用外包,我们需要修改下java源代码。


Working with Log4J is documented inside its manual. Here we use the MyApp-example from the Short Manual [2]. First we have to modify the java source to use the logging framework:

至于怎么使用Log4j,你可以到官网中查找资料。这里我们使用MyApp-example。首先我们要使用这个日志框架去修改java代码。

package oata;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;

public class HelloWorld {
    static Logger logger = Logger.getLogger(HelloWorld.class);

    public static void main(String[] args) {
        BasicConfigurator.configure();
        logger.info("Hello World");          // the old SysO-statement
    }
}

Most of the modifications are "framework overhead" which has to be done once. The blue line is our "old System-out" statement.

Don't try to run ant - you will only get lot of compiler errors. Log4J is not inside the classpath so we have to do a little work here. But do not change the CLASSPATH environment variable! This is only for this project and maybe you would break other environments (this is one of the most famous mistakes when working with Ant). We introduce Log4J (or to be more precise: all libraries (jar-files) which are somewhere under .\lib) into our buildfile:

大多数修改是框架之上的。蓝色部分是旧式日志输出。不要用ant去运行它哦,你会看到输出了很多错误。Log4j外包还没存放到classpath里面,我们还需要做些事情。不过你可不要去修改环境变量里头的CLASSPATH!这么做有可能会破坏其他项目的运行环境,况且我们只需要能够很好的运行这个案例而已。(这是大多数人经常犯的错误,呵呵!)我们现在就开始嵌入log4j到build.xml中。

<project name="HelloWorld" basedir="." default="main">
    ...
    <property name="lib.dir"     value="lib"/>

    <path id="classpath">
        <fileset dir="${lib.dir}" includes="**/*.jar"/>
    </path>

    ...

    <target name="compile">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
    </target>

    <target name="run" depends="jar">
        <java fork="true" classname="${main-class}">
            <classpath>
                <path refid="classpath"/>
                <path location="${jar.dir}/${ant.project.name}.jar"/>
            </classpath>
        </java>
    </target>

    ...

</project>

In this example we start our application not via its Main-Class manifest-attribute, because we could not provide a jarname and a classpath. So add our class in the red line to the already defined path and start as usual. Running antwould give (after the usual compile stuff):

在这个例子中,我们没有通过Main-Class manifest-属性来开始我们的程序,因为我们不能够提供明确的Jar名和classpath。所以我们需要如红色所示添加工程应用程序jar包,然后使用ANT RUN 运行。通常结果显示如下。

[java] 0 [main] INFO oata.HelloWorld  - Hello World

What's that?

  • [java] Ant task running at the moment
  • ant 开始运行
  • 0 sorry don't know - some Log4J stuff
  • log4j里的东东。
  • [main] the running thread from our application
  • 我们应用的主线程
  • INFO log level of that statement
  • 输出语句的等级
  • oata.HelloWorld source of that statement
  • 语句的源文件
  • - separator
  • Hello World the message

For another layout ... have a look inside Log4J's documentation about using other PatternLayout's.

还有另外一种部署方式,可以参考Log4j的文档,找到关于PatternLayout的内容。

作者补充:关于这一块我也不了解,嘻嘻。

Configuration files

配置文件

Why we have used Log4J? "It's highly configurable"? No - all is hard coded! But that is not the debt of Log4J - it's ours. We had coded BasicConfigurator.configure(); which implies a simple, but hard coded configuration. More comfortable would be using a property file. In the java source delete the BasicConfiguration-line from the main() method (and the related import-statement). Log4J will search then for a configuration as described in it's manual. Then create a new file src/log4j.properties. That's the default name for Log4J's configuration and using that name would make life easier - not only the framework knows what is inside, you too!

为什么我们使用Log4j?因为它高度可配置化?不是的-我们已经硬编码了!不过这不是log4j的责任,而在于我们。我们使用了硬编码BasicConfigurator.configure()。虽然很简单,但是很硬。更多的时候,我们会使用属性文件。现在删掉源文件中的硬编码,记得相关的Import语句也要删除。Log4j会自动查找这个属性文件的。好的,我们开始干活了,在src目录下创建log4j.properties文件。这是默认的属性文件名。


log4j.rootLogger=DEBUG, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%m%n

This configuration creates an output channel ("Appender") to console named as stdout which prints the message (%m) followed by a line feed (%n) - same as the earlier System.out.println() :-) Oooh kay - but we haven't finished yet. We should deliver the configuration file, too. So we change the buildfile:

这个配置创建了一个名为stdout的控制台输出,它可以打印出后面跟着“-”的信息,就像System.out.println();语句。注意,我们只是转移了非java文件哦。build.xml修改如下。

    ...
    <target name="compile">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
        <copy todir="${classes.dir}">
            <fileset dir="${src.dir}" excludes="**/*.java"/>
        </copy>
    </target>
    ...

This copies all resources (as long as they haven't the suffix ".java") to the build directory, so we could start the application from that directory and these files will included into the jar.

Testing the class

测试这个类

In this step we will introduce the usage of the JUnit [3] testframework in combination with Ant. Because Ant has a built-in JUnit 3.8.2 you could start directly using it. Write a test class in src\HelloWorldTest.java:

在这一步中,我们将要使用Junit(3) 这个测试框架。由于ant已经集成了这个测试框架,所以我们直接使用它。好啦,现在编写测试类src\HelloWorldTest.java 。

public class HelloWorldTest extends junit.framework.TestCase {

    public void testNothing() {
    }
    
    public void testWillAlwaysFail() {
        fail("An error message");
    }
    
}

Because we dont have real business logic to test, this test class is very small: just show how to start. For further information see the JUnit documentation [3] and the manual of junit task. Now we add a junit instruction to our buildfile:

修改build.xml

    ...

    <path id="application" location="${jar.dir}/${ant.project.name}.jar"/>

    <target name="run" depends="jar">
        <java fork="true" classname="${main-class}">
            <classpath>
                <path refid="classpath"/>
                <path refid="application"/>
            </classpath>
        </java>
    </target>
    
    <target name="junit" depends="jar">
        <junit printsummary="yes">
            <classpath>
                <path refid="classpath"/>
                <path refid="application"/>
            </classpath>
            
            <batchtest fork="yes">
                <fileset dir="${src.dir}" includes="*Test.java"/>
            </batchtest>
        </junit>
    </target>

    ...

We reuse the path to our own jar file as defined in run-target by giving it an ID and making it globally available. The printsummary=yes lets us see more detailed information than just a "FAILED" or "PASSED" message. How much tests failed? Some errors? Printsummary lets us know. The classpath is set up to find our classes. To run tests thebatchtest here is used, so you could easily add more test classes in the future just by naming them *Test.java. This is a common naming scheme.

我们单独创建一个id为application的path task,为了能够全局复用它。Junit有个printsummary属性,名表其意,这个属性设置为yes时,log4j会打印更多的结论信息,比如“失败了”还是“成功了”,多少个测试案例失败了,他们又是哪些。classpath指明了依赖包。通过batchtest这个名为批量测试的东西,你可以在将来扩展测试,只要这些测试类以Test.java结尾命名就可以了。这是常用的命名规范哦。

作者注释:我下了最新的ant,但里面并没有集成类

junit.framework.TestCase,
但这并没有影响原文作者的意图,你可以下个junit-3.8.2.jar包放入Lib下就可以了。

After a ant junit you'll get:

运行结果

...
junit:
    [junit] Running HelloWorldTest
    [junit] Tests run: 2, Failures: 1, Errors: 0, Time elapsed: 0,01 sec
    [junit] Test HelloWorldTest FAILED

BUILD SUCCESSFUL
...

We can also produce a report. Something that you (and other) could read after closing the shell .... There are two steps: 1. let <junit> log the information and 2. convert these to something readable (browsable).

我们也要生成报告。通常在关闭shell之后,我们还需要阅读测试报告。总的来说分为两步:1.记录测试数据。2.把这个数据转换成人可以看到懂的文档。

    ...
    <property name="report.dir"  value="${build.dir}/junitreport"/>
    ...
    <target name="junit" depends="jar">
        <mkdir dir="${report.dir}"/>
        <junit printsummary="yes">
            <classpath>
                <path refid="classpath"/>
                <path refid="application"/>
            </classpath>
            
            <formatter type="xml"/>
            
            <batchtest fork="yes" todir="${report.dir}">
                <fileset dir="${src.dir}" includes="*Test.java"/>
            </batchtest>
        </junit>
    </target>
    
    <target name="junitreport">
        <junitreport todir="${report.dir}">
            <fileset dir="${report.dir}" includes="TEST-*.xml"/>
            <report todir="${report.dir}"/>
        </junitreport>
    </target>

Because we would produce a lot of files and these files would be written to the current directory by default, we define a report directory, create it before running the junit and redirect the logging to it. The log format is XML sojunitreport could parse it. In a second target junitreport should create a browsable HTML-report for all generated xml-log files in the report directory. Now you can open the ${report.dir}\index.html and see the result (looks something like JavaDoc).
Personally I use two different targets for junit and junitreport. Generating the HTML report needs some time and you dont need the HTML report just for testing, e.g. if you are fixing an error or a integration server is doing a job.

因为要生成很多文件,所以我创建了一个目录来存放它们。这个创建的动作需要放到junit指令运行前。运行junit指令后,会产生一个xml文件,junitreport可以解析它。运行junitreport指令,可以解析这个xml文件,并且生成一堆了文件,其中包括index.html。用浏览器运行这个首页文件,就可以看到类似java文档的测试报告了。通常生成HTML文档需要很长的时间,所以我分开成了两个指令。而且,你一般不需要html的测试报告,只要xml的报告就可以了。

Resources

    [1] http://www.apache.org/dist/logging/log4j/1.2.13/logging-log4j-1.2.13.zip
    [2] http://logging.apache.org/log4j/docs/manual.html
    [3] http://www.junit.org/index.htm
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值