官方Manual链接: http://ant.apache.org/manual/index.html
Ant、XSD、JAXB、XML的基本概念这里就不介绍,网上随便搜搜都有一大把,本文主要讲解利用XSD生成JAXB类来自定义Ant Task,自动完成XML的解析工作,提高开发效率。
开发工具采用Eclipse。
第一步,在Eclipse中创建Java项目,这里取名为WritingAntTaskDemo。
第二步,创建xsd文件,这里我在项目中jet.demo.xsd的包下创建了名为MyTaskConfig.xsd文件。
下面是该xsd的Source配置信息(design中的图形界面,这里就不截图了):
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/patternConfig" <span style="white-space:pre"> </span>xmlns:tns="http://www.example.org/patternConfig" elementFormDefault="qualified"> <complexType name="MyTaskConfig"> <span style="white-space:pre"> </span><sequence> <span style="white-space:pre"> </span><element name="message" type="tns:Message" maxOccurs="unbounded" <span style="white-space:pre"> </span>minOccurs="0"> <span style="white-space:pre"> </span></element> <span style="white-space:pre"> </span><element name="description" type="tns:Description" <span style="white-space:pre"> </span>maxOccurs="1" minOccurs="0"> <span style="white-space:pre"> </span></element> <span style="white-space:pre"> </span></sequence> <span style="white-space:pre"> </span><attribute name="id" type="string"></attribute> </complexType> <element name="myTaskConfig" type="tns:MyTaskConfig"></element> <complexType name="Message"> <span style="white-space:pre"> </span><attribute name="id" type="string"></attribute> <span style="white-space:pre"> </span><attribute name="content" type="string"></attribute> </complexType> <complexType name="Description"> <span style="white-space:pre"> </span><attribute name="id" type="string"></attribute> <span style="white-space:pre"> </span><attribute name="content" type="string"></attribute> </complexType> </schema>
根元素是myTaskConfig,myTaskConfig中有个一个id属性,有若干个个message子元素,message子元素中有两个属性,分别是id和content。myTaskConfig还有0个或者1个的description子元素,里面的属性跟message一样。
第三步,利用xsd生成JAXB类,先在工程中创建名为jet.demo.model的包,这里会用来存放生成的JAXB类。右键点击MyTaskConfig.xsd文件,选择Generate->JAXB Classes...。
选择WritingAntTaskDemo项目,在Package中选择jet.demo.model包,然后一直Next,最后Finish就行了,这个时候弹出一个警告框,如下图:
这里是提醒我们,生成的类文件将会覆盖已经存在的文件,问我们确不确定这么干,这里我们选择Yes,不然没法生成的。或者直接把选择框Do not show this message again勾上,一了百了,但是我不建议这么干,因为有时候误操作,会坏事。
接着,在jet.demo.model包中会生成所有在xsd中配置好的类文件,以下是console控制台的输出信息:
parsing a schema...
compiling a schema...
jet\demo\model\Description.java
jet\demo\model\Message.java
jet\demo\model\MyTaskConfig.java
jet\demo\model\ObjectFactory.java
jet\demo\model\package-info.java
Messge.java就是前面XSD中的Message子元素,MyTaskConfig就是前面XSD中的MyTaskConfig根元素。这里ObjectFactory.java以及package-info.java我们都用不着,这里也不过多介绍,想要了解的网友自己去看好了。
Message.java
// // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> // Any modifications to this file will be lost upon recompilation of the source schema. // Generated on: 2016.04.12 at 10:14:00 AM CST // package jet.demo.model; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; /** * <p>Java class for Message complex type. * * <p>The following schema fragment specifies the expected content contained within this class. * * <pre> * <complexType name="Message"> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> * <attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" /> * <attribute name="content" type="{http://www.w3.org/2001/XMLSchema}string" /> * </restriction> * </complexContent> * </complexType> * </pre> * * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Message") public class Message { @XmlAttribute(name = "id") protected String id; @XmlAttribute(name = "content") protected String content; /** * Gets the value of the id property. * * @return * possible object is * {@link String } * */ public String getId() { return id; } /** * Sets the value of the id property. * * @param value * allowed object is * {@link String } * */ public void setId(String value) { this.id = value; } /** * Gets the value of the content property. * * @return * possible object is * {@link String } * */ public String getContent() { return content; } /** * Sets the value of the content property. * * @param value * allowed object is * {@link String } * */ public void setContent(String value) { this.content = value; } }
在Ant Manual中,Developing with Apache Ant章节里的Writing Tasks中有关于属性介绍:
It is very easy - for each attribute provide a public void set<attributename>
(<type>
newValue) method and Ant will do the rest via
reflection.
--------------------------------------------------------------------------------------------------------------------------------------------------------------
大概意思就是说,每个属性要提供一个set方法。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Message子元素中只有两个属性,并且JAXB类中已经自动生成set和get方法,所以我们不需要任何修改,是不是很方便啊。
Description跟Message是一样的,这里就不复制了。
Writing Tasks中有关于子元素的介绍(http://ant.apache.org/manual/develop.html#nested-elements):
Now you have a class NestedElement
that is supposed to be used for your nested <inner>
elements, you have three options:
public NestedElement createInner()
public void addInner(NestedElement anInner)
public void addConfiguredInner(NestedElement anInner)
What is the difference?
Option 1 makes the task create the instance of NestedElement
, there are no restrictions on the type. For the options 2 and 3, Ant has to create an instance
of NestedInner
before it can pass it to the task, this means, NestedInner
must have a public
no-arg constructor or a public
one-arg constructor taking a Project class as a parameter. This is the only difference
between options 1 and 2.
The difference between 2 and 3 is what Ant has done to the object before it passes it to the method. addInner
will receive an object directly after the
constructor has been called, while addConfiguredInner
gets the object after the attributes and nested children for this new object have been handled.
What happens if you use more than one of the options? Only one of the methods will be called, but we don‘t know which, this depends on the implementation of your Java
virtual machine.
--------------------------------------------------------------------------------------------------------------------------------------------------------------
大概的意思就是说对于内嵌的元素,有三种选择方式,create方法,add方法,addConfigured方法。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
三种方法JAXB类中都没有,所以需要我们自己去添加,这里我选择add方法,这个方法最简单了....如下:
// Added by hand, for the purpose of ant script running. public void addMessage(Message message) { List<Message> messageList = getMessage(); messageList.add(message); } public void addDescription(Description description) { this.description = description; } // End of adding.
对于单个的子元素,直接赋值即可。对于List类型的子元素,要采用添加的方式。
修改完的MyTaskConfig.java
// // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> // Any modifications to this file will be lost upon recompilation of the source schema. // Generated on: 2016.04.12 at 10:37:45 AM CST // package jet.demo.model; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; /** * <p>Java class for MyTaskConfig complex type. * * <p>The following schema fragment specifies the expected content contained within this class. * * <pre> * <complexType name="MyTaskConfig"> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> * <sequence> * <element name="message" type="{http://www.example.org/patternConfig}Message" maxOccurs="unbounded" minOccurs="0"/> * <element name="description" type="{http://www.example.org/patternConfig}Description"/> * </sequence> * <attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" /> * </restriction> * </complexContent> * </complexType> * </pre> * * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "MyTaskConfig", propOrder = { "message", "description" }) public class MyTaskConfig { protected List<Message> message; @XmlElement(required = true) protected Description description; @XmlAttribute(name = "id") protected String id; // Added by hand, for the purpose of ant script running. public void addMessage(Message message) { List<Message> messageList = getMessage(); messageList.add(message); } public void addDescription(Description description) { this.description = description; } // End of adding. /** * Gets the value of the message property. * * <p> * This accessor method returns a reference to the live list, * not a snapshot. Therefore any modification you make to the * returned list will be present inside the JAXB object. * This is why there is not a <CODE>set</CODE> method for the message property. * * <p> * For example, to add a new item, do as follows: * <pre> * getMessage().add(newItem); * </pre> * * * <p> * Objects of the following type(s) are allowed in the list * {@link Message } * * */ public List<Message> getMessage() { if (message == null) { message = new ArrayList<Message>(); } return this.message; } /** * Gets the value of the description property. * * @return * possible object is * {@link Description } * */ public Description getDescription() { return description; } /** * Sets the value of the description property. * * @param value * allowed object is * {@link Description } * */ public void setDescription(Description value) { this.description = value; } /** * Gets the value of the id property. * * @return * possible object is * {@link String } * */ public String getId() { return id; } /** * Sets the value of the id property. * * @param value * allowed object is * {@link String } * */ public void setId(String value) { this.id = value; } }
第四步,编写自己的Task。
先在项目中导入Ant的jar包:ant.jar 和ant-launcher.jar。然后在jet.demo.task包下新建一个MyTask类,继承ant的torg.apache.tools.ant.Task类。
MyTask.java
package jet.demo.task; import java.util.List; import jet.demo.model.Description; import jet.demo.model.Message; import jet.demo.model.MyTaskConfig; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; public class MyTask extends Task { private MyTaskConfig myTaskConfig; public void addMyTaskConfig(MyTaskConfig myTaskConfig) { this.myTaskConfig = myTaskConfig; } @Override public void execute() throws BuildException { // show Message List List<Message> messageList = myTaskConfig.getMessage(); if (messageList != null) { for (Message message : messageList) { show("message", message.getId(), message.getContent()); } } // show Description Description description = myTaskConfig.getDescription(); if (description != null) { show("description", description.getId(), description.getContent()); } } // show Information of Element private void show(String type, String id, String content) { System.out.println("type=" + type + ",id=" + id + ",content=" + content); } }
其中myTaskConfig就是我们的根元素,同样需要添加add方法,execute就是ant的task所执行的方法,我们可以通过重载这个方法,实现自己的功能。这里仅仅是打印所有的message和description信息。
第五步,编写Ant脚本。在jet.demo.script包下创建Ant脚本,取名为MyTask.xml。
<?xml version="1.0" encoding="UTF-8"?> <project name="WritingAntTaskDemo" default="theTarget"> <target name="theTarget"> <echo message="Begin to run the target..." /> <taskdef name="myTask" classname="jet.demo.task.MyTask"> </taskdef> <myTask> <myTaskConfig> <message id="Mesg1" content="this is message One." /> <message id="Mesg2" content="this is message Two." /> <message id="Mesg3" content="this is message Thress." /> <description id="DescOne" content="this is my description." /> </myTaskConfig> </myTask> </target> </project>
这里project取名为“WritingAntTaskDemo”,默认执行的target是“theTarget”。在project中创建了一个名为“theTarget”的target,作为project的默认执行目标。
在target中先用echo输出一句话,说明targe开始执行。
然后就是配置我们自己定义的Task,采用taskdef标签,name取名为myTask,classname表示我们自定义Task的类名,这里是jet.demo.task.MyTask。
这就是定义我们的参数信息了,“myTask”是与taskdef中的name对应的。里面有一个myTaskConfig,myTaskConfig中有三个message和一个description。
好了,接下来就是执行这个Ant脚本了。
第六步,执行Ant脚本。
在Eclipse菜单栏中选择Run--> Run Configurations--> 创建一个Java Applicatio。
这里取名为MyTask,Project选择WritingAntTaskDeml,Main Class设置为org.apache.tools.ant.Main。
Arguments设置为-f "E:\workplace_smart_luna\WritingAntTaskDemo\src\jet\demo\script\MyTask.xml",-f里的是我们之前编写的Ant脚本的路径,这个大家要自己修改。
选择Apply,然后Run。这个时候可以控制台的输出:
Buildfile: E:\workplace_smart_luna\WritingAntTaskDemo\src\jet\demo\script\MyTask.xml theTarget: [echo] Begin to run the target... [myTask] type=message,id=Mesg1,content=this is message One. [myTask] type=message,id=Mesg2,content=this is message Two. [myTask] type=message,id=Mesg3,content=this is message Thress. [myTask] type=description,id=DescOne,content=this is my description. BUILD SUCCESSFUL Total time: 0 seconds
大功告成。
总结
本文主要讲解如何利用JAXB类,采用自定义Ant Task的方式来读取自定义的XML格式,从而直接生成属性对象。
首先创建XSD,作为xml的schema,的好处是以后修改起来很方便,看起来也很直观。
然后是通过XSD生成JAXB类,以及针对Ant的特性,稍微修改JAXB类,添加add方法。
接着就是编写自己的Task类,需要继承Ant的org.apache.tools.ant.Task类,实现自己的execute方法。
再就是编写Ant的build脚本,配置XML信息。
最后便是运行Ant脚本。
源码下载地址:
https://github.com/siyueshiqi/JetDemos