??xml version="1.0" encoding="utf-8" standalone="yes"?>
XDocletd是Ant的自定义dQ除此以外,没有其他q行XDocletd的方法?br /> XDoclet它有两个重要的组Ӟ
q行Ҏ标记?Java 源文件?
预先定义的模ѝ[引用]
Merge File用来处理无法在Source Code中加xdoclet tag的情c?br />
Ø XDoclet中的核心dQ?br /> <ejbdoclet>Q面向EJB领域Q生成EJB、工L和布|描q符?br /> <webdoclet>Q面向Web开发,生成serlvet、自定义标签库和web框架文g?br /> <hibernatedoclet>QHibernate持箋Q配|文件、Mbeans
<jdodoclet>QJDOQ元数据Qvender configuration
<jmxdoclet>QJMXQMBean接口QmletsQ配|文件?br /> <doclet>Q用用戯定义模板来生成代码?br /> <documentdoclet>Q生成项目文Ӟ例如todo列报表)
Ø webdoclet sub task
XDocletq没有和Ant一起发布,所以如果你惌使用XDoclet的话Q就需要单独的下蝲?br /> 安装。在使用M一个XDoclet的Q务之前,你首先需要在使用Ant?lt;taskdef>d来声
明它?br /> <deploymentdescriptor>Q生标准的web引用配置文gweb.xmlQdestdir属性设?br /> web.xml文g的存放位|?br /> XDoclet通过合ƈ?merge points)支持定制,合ƈҎ在模板文件定义里允许q行时插入定制代码的地方,使用mergedir属性设|?br /> <target name="webdoclet" depends="compile-web"
unless="webdoclet.unnecessary"
description="Generate web and Struts descriptors">
<taskdef name="webdoclet" classname="xdoclet.modules.web.WebDocletTask">
<classpath>
<path refid="xdoclet.classpath"/>
<path refid="web.compile.classpath"/>
</classpath>
</taskdef>
<!—mergedir完成模板文g合ƈ的功?->
<webdoclet destdir="${webapp.target}/WEB-INF"
force="${xdoclet.force}"
mergedir="metadata/web"
excludedtags="@version,@author"
verbose="true">
<!—找出目录src/web ?{build.dir}/web/gen文g中的webdoclet?br /> 释,生成web.xml,struts-config.xml{配|信息文?->
<fileset dir="src/web"/>
<fileset dir="${build.dir}/web/gen"/>
<!—生成的web.xml文g攑֜build/appName/WEB-INF/-->
<deploymentdescriptor validateXML="true"
servletspec="2.3" sessiontimeout="10"
destdir="${webapp.target}/WEB-INF" distributable="false"
displayname="${ant.project.name}">
<configParam name="httpPort" value="${http.port}"/>
<configParam name="httpsPort" value="${https.port}"/>
<configParam name="daoType" value="${dao.type}"/>
<configParam name="security" value="${security.mode}"/>
</deploymentdescriptor>
<jsptaglib validateXML="true"
description="Custom tag library for this application"
shortName="${webapp.name}" filename="${webapp.name}.tld"/>
<strutsconfigxml validateXML="true" version="1.2"/>
<strutsvalidationxml/>
</webdoclet>
</target>
图表 1引用一张网友发表的非常直观的用说明图
Ø XDoclet 中的合ƈ?
?XDoclet 的文中Q您会非帔RJ地看到术语 合ƈ点(merge pointQ和 合ƈ文gQmerge fileQ。合q文件是文本文gQ您可以把它合ƈ?XDoclet 生成代码的指定位|——“合q点”上Q由模板指定Q。可以用合ƈ文g来包含静态文本(例如代码片断?XML 片断Q,q些文本可能很难或者不能用 XDoclet 的能力生成。例如,在示例代码的 metadata/web 目录下,您会扑ֈq些文g。在代码生成期间Q可以用到这些文件合q|文件的一部分[引用]?
ØXDoclet中的模板
XDoclet使用代码模板来生成代码。模?template)是你想生成文件的原型。模杉K?br /> 用一些XML标签来指导模板引擎如何根据输入类以及它们的元数据来调整代码的生成?br /> 模板有点像JSP文g。它们都包含文g和XML标签Q生成输出文件时XML标签会被解析Q?br /> 然后生成文本q显C在XML标签所处的位置上。除了以XDt为命名空间打头的XML标签
会被XDoclet引擎解析以外Q其余的XML标签XDoclet会忽略不?br /> Ø AppFuse中生成ActioncȝXDoclet模板
public final class <XDtClass:className/>Action extends BaseAction {
public ActionForward cancel(ActionMapping mapping, ActionForm form,HttpServletRequest request,
HttpServletResponse response)
throws Exception {
return search(mapping, form, request, response);
}
public ActionForward delete(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("Entering 'delete' method");
}
ActionMessages messages = new ActionMessages();
<XDtClass:className/>Form <XDtForm:classNameLower/>Form = (<XDtClass:className/>Form) form;
// Exceptions are caught by ActionExceptionHandler
Manager mgr = (Manager) getBean("manager");
mgr.removeObject(<XDtClass:className/>.class, new Long(<XDtForm:classNameLower/>Form.getId()));
messages.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("<XDtForm:classNameLower/>.deleted"));
// save messages in session, so they'll survive the redirect
saveMessages(request.getSession(), messages);
return search(mapping, form, request, response);
}
??br /> }
Ø AppFuse中自动生成的对应Actionc?br /> public final class PersonAction extends BaseAction {
public ActionForward cancel(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
return search(mapping, form, request, response);
}
public ActionForward delete(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("Entering 'delete' method");
}
ActionMessages messages = new ActionMessages();
PersonForm personForm = (PersonForm) form;
// Exceptions are caught by ActionExceptionHandler
Manager mgr = (Manager) getBean("manager");
mgr.removeObject(Person.class, new Long(personForm.getId()));
messages.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("person.deleted"));
// save messages in session, so they'll survive the redirect
saveMessages(request.getSession(), messages);
return search(mapping, form, request, response);
}
??br /> }
]]>
XDoclet是一个代码生成工P它可以把你从Java开发过E中J重的重复劳动中解脱出来。XDoclet可以让你的应用系l开发的更加快速,而你只要付比原先更少的努力。你可以把你手头上的冗长而又必需的代码交l它帮你完成Q你可以逃脱“deployment descriptor地狱”,你还可以使你的应用系l更加易于管理。而你所要做的,只不q是在你的注释里Q多加一些类javadoc属性。然后,你会惊讶于XDocletZ做到的一切?br />讨论XDocletQ有一Ҏ较容易生淆,那就是XDoclet不但是一pȝ的代码生成应用程序,而且它本w还是一个代码生成框架。虽然每个应用系l的l节千变万化Q比如EJB代码生成和Struts代码生成是不一LQ而JMX代码生成又是另一番景象)Q但q些代码生成的核心概念和用法却是cM的?br />在这一章里Q我们将会看到渗透到所有XDoclet代码生成E序当中的XDoclet框架基础概念。但在之前,我们先从一个例子入手?br />
2.1 XDoclet in action
每一个程序员都会认识刎ͼ他们的程序永q也不会完成。M有另一些的功能需要添加,另一些的BUG需要修正,或者需要不断的q行重构。所以,在代码里d注释Q提醒自己(或者其他的E序员)有哪些Q务需要完成已成ؓ一个共识?br />如何来跟t这些Q务是否完成了呢?理想情况下,你会攉整理出来一个TODOd列表。在q方面,XDoclet提供了一个强大的TODO生成器,来帮助你完成q个d。这是一个把XDoclet引入目的好Z?br />2.1.1 一个公qd
假设你正在开发一个用了勺子的类?br />public class Matrix {
// TODO ?需要处理当没有勺子的情?br /> public void reload() {
// ...
Spoon spoon = getSpoon();
// ...
}
}
理想情况下,你在下一ơ阅读这D代码的时候,你会处理q个“空勺子?null spoon)的问题。但如果你过了很久才回来看这D代码,你还会记得在q个c里q有一些工作要做吗Q当Ӟ你可以在你的源码里全局搜烦TODOQ甚至你的集成开发环境有一个内建的TODO列表支持。但如果你想把Q务所在的cdҎ也标注出来的话,XDoclet可以是另一U选择。XDoclet可以Z的项目生成一个TODO报表?br />
2.1.2 dXDoclet标签
Z把你的TODO目转换成另一U更加正式的格式Q你需要对代码q行一些细微的改动。如下所C:
public class Matrix {
/** @todo 需要处理当没有勺子的情?*/
public void reload() {
// ...
}
}
q里加入了一个XDoclet需要的cjavadoc标签。XDoclet会用这些标{标记的信息Q以及在q种情况下标{所处的cdҎQ来生成TODO报表?br />
2.1.3 与Ant集成
要生成TODO报表Q你需要确保在你的机器上正安装了XDoclet?br />在Antd里,最要包含一个目标(例如init目标Q定?lt;documentdoclet>dQ这是一个Ant自定义Q务,例如Q?br /> <taskdef name=”documentdoclet?br /> classname=”xdoclet.modules.doc.DocumentDocletTask?br /> classname=”xdoclet.lib.path?/>
q个<documentdoclet>d是XDoclet核心代码生成应用E序中的一个?br />现在Q你可以在Ant构徏文g中加入一个todo目标调用q个d来生成TODO报表Q如Q?br /> <target name=”todo?depends=”init?gt;
<documentdoclet destdir=”todo?gt;
<fileset dir=?{dir.src}?gt;
<include name=?*/*.java?/>
</fileset>
<info/>
</documentdoclet>
</target>
<info>子Q务会遍历你的源文Ӟ查找todo标签Qƈ在todo子目录下生成HTML格式的TODO报表?br />
2.1.4 创徏一个更加职业化的TODO报表
XDoclet生成的TODO报表可以有一个更加职业化的外表。报表会列出一个概览,昄在哪个包哪个c里有todo(以及todo的个数Q。Todo可以跟在方法、类和域上,从报表上可以清楚的区别它们。类U别的todo会标注classQ方法别的todo会在方法签名上标注M。构造函数和域相关的todo也会进行相似的标注?br />q个d看v来很单,但考虑C所需要做的只是在注释上添加一些格式化的@todo标签Q相对于那种只有人才可以理解的无格式的松散的注释Q这U标{是机器可读的,也更Ҏ~程处理。生成的输出也更Ҏ阅读q且更加的商业化?br />
2.2 d和子d
生成todo报表Q只是XDoclet可以完成的事情当中的冰山一角。当初,XDoclet因ؓ可以自动生成EJBJ杂的接口和布v描述文g而声名鹊赗然而,现在的XDoclet已经发展成了一个全功能的、面向属性的代码生成框架。J2EE代码生成只是XDoclet的一个应用方面,它可以完成的d已经q远越了J2EE和项目文的生成?br />
2.2.1 XDoclet d
到现在ؓ止,我们一直在讨论使用XDoclet生成代码Q但事实上,更确切的说法应该是,我们使用XDoclet的一个特定的d来生成代码,比如<ejbdoclet>。每一个XDocletdx于一个特定的领域Qƈ提供q个领域的丰富的代码生成工具?br />[定义QQ务(TasksQ是XDoclet里可用的代码生成应用E序的高层概c]
在XDoclet里,目前已有如下所C的七个核心d?br /><ejbdoclet>Q面向EJB领域Q生成EJB、工L和布|描q符?br /><webdoclet>Q面向Web开发,生成serlvet、自定义标签库和web框架文g?br /><hibernatedoclet>QHibernate持箋Q配|文件、Mbeans
<jdodoclet>QJDOQ元数据Qvender configuration
<jmxdoclet>QJMXQMBean接口QmletsQ配|文件?br /><doclet>Q用用戯定义模板来生成代码?br /><documentdoclet>Q生成项目文Ӟ例如todo列报表)
q其中,<ejbdoclet>最常用Qƈ且很多项目也仅仅使用XDoclet来进行EJB代码生成?lt;webdoclet>是其ơ一个常用的代码生成d。当Ӟ在一个项目中同时使用几个XDocletd是可能的Qƈ且也是推荐的Q,但在q些d之间是完全独立的Q它们彼此之间ƈ不能q行直接的交?br />
2.2.2 XDoclet子Q?br />XDoclet的Q务是领域相关的,而在某个特定领域的XDocletdQ又p许多多紧密耦合在一L子Q务组成的Q这些子d每个都仅仅执行一个非常特定和单的代码生成d?br />[定义Q子dQsubtasksQ是׃Q务提供的单目标的代码生成q程]
d提供子Q务执行时的上下文Qƈ且把q些相关的子dl织理了v来。Q务会依赖q些子Q务来生成代码。在一个Q务当中调用多个子d来协同完成各U各h较大型的代码生成d是非常常见的。比如,在开发EJBӞ你可能想要ؓ每一个bean生成一个home接口Q一个remote接口以及ejb-jar.xml布v描述W文件。这是?lt;ejbdoclet>d的上下文环境中的三个独立的代码生成子d?br />子Q务可以随意的l合排列Q以满目代码生成的需要。某个XDocletd包含的子dl常会共享功能和在源文g中用相同的XDoclet标签。这意味着当你开始一个Q务的时候,你可以很Ҏ的集成进一个相关的子Q务,而不需要很大的改动?br />
子Q务交?br />让我们以<ejbdoclet>dZQ看一下相关的子Q务之间是如何q行兌的。假设你正在开发一个CMPQ容器管理持久化Q实体Bean。你惌使用一?lt;ejbdoclet>的子dQ?br />?lt;deploymentdescriptor>Q生成ejb-jar.xml布v描述W文件?br />?lt;localhomeinterface>Q生成local home接口?br />?lt;localinterface>Q生成local接口?br />在执行如上子d的时候,你需要标记出你的实体Bean的CMP域。当你发布你的bean的时候,你还需要在开发商相关的布|描q符中提供某个特定的关系数据库中的特定表和列与你的CMP实体Bean的映关pRXDoclet可以让你在原先已存在的CMP XDoclet属性基上再加上一些关pL属性,然后Q你可以在d中加入一个开发商相关的子dQ例?lt;jboss>或?lt;weblogic>Q来生成布v描述W文件。XDoclet提供了几乎所有的应用服务器的支持Q你只需要一些初始化的小改动Q就可以q行q些应用服务器相关的代码生成了?br />但那只是冰山一角。你q可以?lt;entitycmp>子Q务ؓZ的bean生成一个实体bean接口的实现子cR如果你使用<valueobject>子Q务来Z你的bean生成值对象,<entityemp>子Q务还会ؓ你的值对象生成方法的实现代码?br />觉得不可思议了吧。可惜XDoclet没有提供<cupofcoffee>子Q务,要不然我们可以喝杯咖啡,休息一下啦?br />q里不是惛_你介l?lt;ejbdoclet>所有的子Q务或?lt;ejbdoclet>可以完成的所有代码生成功能,而仅仅是惛_你展CZ下Q务的子Q务之间是如何工作在一L。一旦你开始ƈ熟悉了一个XDoclet 子Q务,熟悉另一个子d会变得非常简? 那种每个子Q务都是孤立的相比Q用这U可以相互协作的子Q务,开发成本会显著的降低,效果也更加的立竿见媄?
2.3 使用Ant执行d
XDoclet“嫁”给了Ant。XDocletd是Ant的自定义dQ除此以外,没有其他q行XDocletd的方法。所q的是,Ant已经成ؓ了Java构徏工具事实上的标准Q所以这不算什么限制。事实上Q反q来QXDoclet与Ant的这U“亲密”关pM得XDoclet可以参与CQ何Ant构徏q程当中厅R?br />2.3.1 声明d
XDocletq没有和Ant一起发布,所以如果你惌使用XDoclet的话Q就需要单独的下蝲和安装。在使用M一个XDoclet的Q务之前,你首先需要在使用Ant?lt;taskdef>d来声明它。例如:
<taskdef name=”ejbdoclet?br /> classname=”xdoclet.modules.ejb.EjbDocletTask?br /> classpathref=”xdoclet.lib.path?>
如果你熟悉Ant的话Q你׃知道q段代码是告诉Ant加蝲<ejbdoclet>的Q务定义。当Ӟ你也可以以你喜欢的Q何方式来命名q个自定义Q务,但最好还是遵守标准的命名规律以免发生h。classname和classpathref属性告诉Ant到哪里去扑֮现这个自定义d的XDocletcR如果你想用其他的XDocletdQ就必须要类D样首先声明这个Q务?br />一般共通的做法是,把所有需要用的XDocletd都放在Ant的一个目标里声明Q这样在其他的目标里如果需要用这些Q务,只要dependsq个d可以了。你可能已经在Ant的构建文仉包含了init目标Q这是攄XDocletd声明的好地方Q当然如果你没有Q你也可以徏一个)。下面的例子是在一个init目标里加入了<ejbdoclet>?lt;webdoclet>的声明:
<target name=”init?gt;
<taskdef name=”documentdoclet?br /> classname=”xdoclet.modules.doc.DocumentDocletTask?br /> classpathref=”xdoclet.lib.path?/>
<taskdef name=”ejbdoclet?br /> classname=”xdoclet.modules.ejb.EjbDocletTask?br /> classpathref=”xdoclet.lib.path?/>
<taskdef name=”webdoclet?br /> classname=”xdoclet.modules.web.WebDocletTask?br /> classpathref=”xdoclet.lib.path?/>
</target>
现在QQ务声明好了,XDoclet“整装待发”?br />
2.3.2 使用d
你可以在M目标里用声明好的Q务。在d的上下文环境里,可以调动相关的子d。让我们看一个例子,q个例子调用?lt;ejbdoclet>d。不要担心看不懂语法的细节,现在你只需要关心一些基概念可以了?br /><target name=”generateEjb?depends=”init?gt;
<ejbdoclet destdir=?{gen.src.dir}?gt;
<fileset dir=?{src.dir}?gt;
<include name=?*/*Bean.java?>
</fileset>
<deploymentdescriptor destdir=?{ejb.deployment.dir}?>
<homeinterface/>
<remoteinterface/>
<localinterface/>
<localhomeinterface/>
</ejbdoclet>
</target>
把Q务想像成一个子E序q行旉要的一个配|环境(CQ子d才是真正q行代码生成工作的)。当调用一个子dӞ子Q务从dl承上下文环境,当然Q你也可以根据需要随意的覆盖q些倹{在上面的例子里Q因?lt;deploymentdescriptor>子Q务生成的布v描述W文件和其他生成各种接口的子d生成的Java源文仉要放在不同的位置Q所以覆盖了destdir的属性倹{布|描q符文g需要放在一个在打包EJB JAR文g的时候可以容易包含进来的地方Q而生成的Java代码则需要放|在一个可以调用Java~译器进行编译的地方。需要这些子d之间是紧密关联的Q但只要你需要,你可以有_的自L控制d的生成环境?br /><fileset>属性同栯应用到所有的子Q务。这是一个Ant的复杂类型(相对于文本和数值的单类型)Q所以以子元素的方式在Q务中声明。不要把它和子Q务Z谈。当Ӟ如果你想在某个子d中另外指定一个不同的输入文g集,你也可以在这个子d中放|一?lt;fileset>子元素来覆盖它?br />子Q务的可配|选项q远不止q些。我们会在下一章l介l所有的d和子dQ以及常用的配置选项?br />
2.4 用属性标注你的代?br />可重用的代码生成pȝ需要输入来生成感兴的输出。一个解析器生成器也需要一个语a描述来解析生成解析器。一个商务对象代码生成器需要领域模型来知道要生成哪些商务对象。XDoclet则需要Java源文件做出来生成相关的类或者布|?配置文g?br />然而,源文件可能ƈ没有提供代码生成所需要的所有信息。考虑一个基于servlet的应用,当你想生成web.xml文g的时候,servlet源文件仅可以提供cd和适当的servlet接口Ҏ。其他的信息比如URI pattern映射、servlet需要的初始化参数等信息q没有涵盖。显而易见,如果classq没有提供这些信息给你,你就需要自己手动在web.xml文g时填写这些信息?br />XDoclet当然也不会知道这些信息。幸q的是,解决Ҏ很简单。如果所需信息在源文g时没有提供,那就提供它,做法是在源文g里加入一些XDoclet属性。XDoclet解析源文Ӟ提取q些属性,q把它们传递给模板Q模板用这些信息生成代码?br />
2.4.1 剖析属?br />XDoclet属性其实就是javadoc的扩展。它们在外表上和使用上都有javadoc属性一P可以攄在javadoc文注释里。文注释以/**开始,*/l尾。下面是一个简单的例子Q?br />/**
* q是一Djavadoc注释?br />* 注释可以被分解成多行Q每一行都以星?img alt="Start" src="http://www.cjsdn.net/images/smiles/star_smile.gif" width="19" />开始?br />*/
在注释里的所有文本都被视为javadoc注释Qƈ且都能够被XDoclet讉K到。注释块一般都与Java源文件中的某个实体有养Iq紧跟在q个实体的前面。没有紧跟实体的注释块将不会被处理。类Q或者接口)可以有注释块Q方法和域也可以有自q注释块,比如Q?br />/**
* cL释块
*/
public class SomeClass {
/** 域注释块 */
private int id;
/**
* 构造函数注释块
*/
public SomeClass() {
// ...
}
/**
* Ҏ注释?br /> */
public int getId() {
return id;
}
}
注释块分成两部分Q描q部分和标签部分。当遇到W一个javadoc标签Ӟ标签部分开始。Javadoc标签也分成两部分Q标{和标{描q。标{描q是可选的Qƈ且可以多行。例如:
/**
* q是描述部分
* @tag1 标签部分从这里开?br />* @tag2
* @tag3 前面一个标{没有标{描q?br />* q个标签有多行标{描q?br />*/
XDoclet使用参数化标{扩展了javadoc标签。在XDoclet里,你可以在javadoc标签的标{描q部分加入name=”value”参数。这个微的改动大大增强了javadoc标签的表达能力,使得javadoc标签可以用来描述复杂的元数据。下面的代码昄了用XDoclet属性描q实体BeanҎQ?br />/**
* @ejb.interface-method
* @ejb.relation
* name=”blog-entries?br />* role-name=”blog-has-entries?br />* @ejb.value-object
* compose=”com.xdocletbook.blog.value.EntryValue?br />* compose-name=”Entry?br />* members=”com.xdocletbook.blog.interfaces.EntryLocal?br />* members-name=”Entries?br />* relation=”external?br />* type=”java.util.Set?br />*/
public abstract Set getEntries();
参数化标{օ许组合逻辑上相兌的属性。你可以加入描述q个cȝ元信息,使得q个cȝ信息_生成代码。另外,E序员借由阅读q样的元信息Q可以很快的理解q个cL如何使用的。(如果q个例子上的元信息你看不懂,不要担心Q在W?章里Q我们会学到EJB相关的标{以及它们的涉|。)
另外Q请注意上面的例子中Q所有的标签名都以ejb开头。XDoclet使用namespace.tagname的方式给标签提供了一个命名空间。这样做除了可以跟javadoc区别开来以外,q可以把d相关的标{lv来,以免d之间的标{生淆?
2.5 代码生成模式
XDoclet是一U基于模板的代码生成引擎。从高层视图上来看,输出文g其实是p析执行各式各L模板生成出来的。如果你理解了模板以及它所执行的上下文环境Q就可以切的认识到QXDoclet可以生成什么,不可以生成什么。如果你正在评估XDocletq_Q理解这些概忉|非常重要的。要不然Q你可能会错qXDoclet的许多强大的功能Q也可能会被XDoclet的一些限制感到迷惑?br />XDocletq行在在Ant构徏文g环境中,它提供了Ant自定义Q务和子Q务来与XDoclet引擎交互。Q务是子Q务的容器Q子d负责执行代码生成。子d调用模板。模板提供了你将生成代码的饼q模子。XDoclet解析输入的源文gQ提取出源文件中的XDoclet属性元数据Q再把这些数据提供给模板Q驱动模板执行。除此之外,模板q可以提供合q点(merge points)Q允许用h入一些模板片?合ƈ文gmerge files)来根据需要定制代码生成?br />2.5.1 模板基础
XDoclet使用代码模板来生成代码。模?template)是你想生成文件的原型。模杉K使用一些XML标签来指导模板引擎如何根据输入类以及它们的元数据来调整代码的生成?br />[定义Q模?template)是生成代码或描述文g的抽象模视图。当模板被解析的时候,指定的细节信息会被填入。]
模板一般情况下会有一个执行环境。模板可能应用在一个类环境(转换生成transform generation)Q也有可能应用在一个全局环境(聚集生成aggregate generation)。{换生成和聚集生成是XDoclet的两U类型的d模式Q理解它们之间的区别对于理解XDoclet是非帔R要的?br />当你使用XDoclet生成布置描述W文件时Q你使用的是聚集生成。布|描q符文gq不仅仅只与一个类相关Q相反,它需要从多个c里聚集信息C个输入文件。在q种生成模式里,解析一ơ模板只会生成一个输出文Ӟ不管有多个输入文g?br />在{换生成模式里Q模杉K到每一个源文g׃解析一ơ,Ҏ该文件类的上下文环境生成输出。这U生成模式会为每一个输入文件生成一个输出文件?br />转换生成模式的一个很好的例子是生成EJB的local和remote接口。显Ӟ接口是和BeancM一相关的。从每一个类里提取信?cM及它的方法、域、接口以及XDoclet属性等信息)转换出接口。除此以外,不需要其他的信息?br />从实现里提取出接口似乎有点反向。如果你手写E序的话Q一般来说会先定义一个接口,然后再写一个类来关现它。但XDoclet做不刎ͼXDoclet不可能帮你实C个已有接口,因ؓ它不可能帮你生成你的业务逻辑。当Ӟ如果业务逻辑可以从接口本w得?比如JavaBean的get/set讉K?或者用XDoclet属性声明好Q那么生成业务逻辑代码来实C个接口也不是不可能。但一般情况下Q这样做不太现实。相比而言Q提供一个实玎ͼq描q接口与q个实现之间的关联就Ҏ多了?br />聚集生成和{换生成主要区别在它们的环境信息上。即使一个代码生成Q务中生成一个Java文gQ一般也不常用聚集生成,因ؓ生成一个Javac还需要一些重要信息如cL处的包以及你想生成的cdQ在q种环境下是无法提供的。如果一定要使用聚集生成的话Q那需要在另一个单独的地方提供好配|信息了?br />2.5.2 模板标签
在还没见到模杉K啥样子之前,我们已经比较深入的认识它了。那模板文gI竟长啥样子呢?它有点像JSP文g。它们都包含文g和XML标签Q生成输出文件时XML标签会被解析Q然后生成文本ƈ昄在XML标签所处的位置上。除了以XDt为命名空间打头的XML标签会被XDoclet引擎解析以外Q其余的XML标签XDoclet会忽略不。下面的代码片断昄了XDoclet模板的“经兔R型”:
public class
<XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/></XDtClass:classOf>
Extends Observabe {
static <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/></XDtClass:classOf>
_instance = null;
public static <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/></XDtClassOf>
getInstance() {
if (_instance == null) {
_instance =
new <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/>
</XDtClass:classOf>();
?br /> return _instance;
}
}
研究一下这个模板,你会发现Q它生成的是一个类定义。这个类里定义了一个静态变量instanceQƈ且用一个静态方法来控制q个静态文件的讉K。借助Java语法Q你可以很容易的推断出那些XDoclet模板标签的目录是生成cdQ虽然对于这个标{֦何工作你qƈ不是很了解?br />即你从没打过要自己写模板Q但理解模板是如何被解析q行的还是很有必要的。迟早你会调用到一个运行失败的XDocletdQ没有生你所期望的输出,那么最快捷的找出原因的Ҏ是直接查模板文Ӟ看看是哪里出了问题?br />让我们看一下生成静态域定义的片断:
static <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/></XDtClass:classOf>
_instance = null;
在XDoclet的眼里,q段模板代码很简单,那就是:
static <tag/> _instance = null;
XDoclet解析执行标签Q如果有输出的话Q把输入|回到文本里厅R有些标{会执行一些运,把输出放回到一个流里。这L标签UC为内Ҏ{?content tags)Q因为它们生内宏V?br />另一U类型的标签UC为BODY标签。BODY标签在开始和l束标签之间存在文本。而BODY标签强大强大在q些文本自己也可以是一断可以由外围标签解析的模板片断。比如在上面的例子里QXDtClass:classOf标签Q它们的内容是模板片断Q?br /> <XDtEjbFacade:remoteFacadeClass/>
classOf标签解析q段模板Q提取出全限制的内容Q然后剃除前面的包面Q只输出cd。BODY标签q不L会解析它的内容,在做qg事之前,它们会事先检查一些外部判断条?比如查检查你正在生成的是一个接口还是一个类)。这里标标签UC为条件标{?conditional tags)。还有一些BODY标签提供cMq代的功能,它的内容会被解析多次。比如一个标{N对类里的每一个方法解析一ơ内宏V?br />XDoclet标签提供了许多高层次的代码生成功能,但是有时候,它们可能昑־不够灉|Q或者表达能力满不了你的需要。这时候,相对于另外开发一套通用功能的模板引擎相比,你可以选择扩展XDoclet模板引擎。你可以使用更具表述能力、功能更加强大的Javaq_开发你自己的一套标{?
2.6 使用合ƈ定制
代码生成pȝ之所以用的不多Q主要原因就在于它们往往只能生成一些死板的、不够灵zȝ代码。大多数代码生成pȝ不允怽改动它们生成的代码;如果Q如果这个系l不够灵z,你所能做到的最好的扩展是应用l承扩展生成的代码,或者用一些共通的设计模式(比如Proxy和Adaptor)来满你的需要。无论如此,q都不是产生你想生成的代码的好办法。代码生成器最好能做到所生成x得WYGIWYG(what you generate is what you get)Q来取代你需要花费大量的旉来粉饰生成出来的q不满要求的代码。所以,对于代码生成器来_支持灉|的定Ӟ是生成能够完全满求的代码的前提条件?br />XDoclet通过合ƈ?merge points)支持定制——合q点是在模板文g定义里允许运行时插入定制代码的地斏V有时候,合ƈ点甚臛_以媄响到全局代码的生成,不但允许你添加一些定制内容,q可以从Ҏ上改变将要生成出来的东西?br />[定义Q合q点(Merge points)是模杉K先定义的允许你在代码生成的运行时加入定制内容的扩展点]
让我们研I一D从XDoclet源代码里摘取出来的模板代码。在为实体Bean生成主键的模板末,定义了这样一个合q点Q?br /> <XDtMerge:merge file=”entitypk-custom.xdt?gt;</XDtMerge:merge>
如果你在你的merge目录下创Z一个名为entitypk-custom.xdt文gQ那么这个模板文件的内容会在这个合q点被包含进来。你的定制可以执行高层模板可以执行的所有强大功能,可以q行所有模板可以进行的q算(包括定义自定义标{,定义它们自己的合q点)?br />上面的这U合q点Qؓ所有的cȝ境用了同一个文件。当Ӟ也可以ؓ每一个类环境使用不同的合q文件。如果你不想定制全部的类文gQ或者你不想Z某些改动而重写模板的时候,q会很有用。不动机是什么,逐类的合q点很容易识别出来:他们会在名字里包含一个XDoclet的逐类标记{0}。这里有一个生成ejb-jar.xml文g里的安全角色引用的例子:
<XDtMerge:merge file=”ejb-sec-rolerefs-{0}.xml?gt;
<XDtClass:forAllClassTags tagName=”ejb:security-role-ref?gt;
<security-role-ref>
<role-name>
<XDtClass:classTagValue
tagName=”ejb:security-roleref?br />paramName=”role-name?>
</role-name>
<role-link>
<XDtClass:classTagValue
tagName=”ejb:security-roleref?br /> paramName=”role-link?>
</role-link>
</security-role-ref>
</XDtClass:forAllClassTags>
</XDtMerge:merge>
q段模板会遍历工E里的所有Bean。对于每一个BeanQXDoclet先从Bean的文件名里提取出Bean名,然后替换{0}Q再Ҏ替换后的文g名去L合ƈ文g。例如,如果你有一个名为BlogFacadeBean的BeanQXDoclet会尝试寻找一个名为ejb-src-rolerefs-BlogFacade.xml的合q文件?br />如果找不到这个合q文Ӟ则这?lt;merge>标签的内Ҏ板会被解析。这意味着合ƈ点不仅可以提供定制内容,q可以在一个模板文仉定义一个替换点Q当定制内容不存在的时候用替换点里的内容。不是所有的XDocletd都提供了有替换内容的合ƈ点,一般来_它们更們于只提供一个简单的合ƈ点,仅仅当合q文件存在的时候解析ƈ导入合ƈ文g的内宏V这取决于Q务的开发者觉得哪U合q点更符合他的要求?br />q有一Ҏ有介l到的是XDoclet如何定位合ƈ文gQ每一个XDocletd或者子d都会提供一个mergeDir属性,q个属性用于设|你存放合ƈ文g的目录?
<ccid_code>private HtmlInputText txtUsername = new HtmlInputText(); |
<ccid_code>/** |
<ccid_code>public javascriptSubTask() |
<ccid_code>/* |
<ccid_code><target name="javascript" depends="jxdoc_init" |
<ccid_code><XDtClass:forAllClasses> 遍历所有含有标{c(在ant中指定) |
<ccid_code><?xml version="1.0" encoding="UTF-8"?> |
<ccid_code><xdoclet> |
<ccid_code>?lt;level> method </level>”代表该标签出现在方法上而不是类之上。例? |
XDoclet支持的SpringҎ注入包括QrefQlistQname和value。遗憄是,在某个项目中Q我需要注入一个包含Bean引用的ListQ?/p>
public void setHandlers(List handlers) {
...
}
然后QXDocletq不支持元素为引用类型的ListQ倘若使用下列注释Q?/p>
/**
* @spring.property list="articleHandler,imageHandler"
*/
public void setHandlers(List handlers) {
...
}
生成的配|文件如下:
<property name="handlers">
<list>
<value>articleHandler</value>
<value>imageHandler</value>
</list>
</property>
毫无疑问Q在Spring启动Ӟ一个ClassCastException被抛出Q因为无法将Stringcd转化为我们自定义的Handler引用cd?/p>
q运的是QXDoclet良好的可扩展性我们能够L扩展需要的配置Q甚至不需要我们利用XDoclet提供的API~写代码。XDoclet提供一UXMLl构的模版语a来生成配|文Ӟ对于Spring配置文gQ对应的XML配置文g?font color="#339966">xdoclet-spring-module-1.2.3.jar/xdoclet/modules/spring/resources/spring_xml.xdt中?/p>
解开jar包,修改spring_xml.xdtQ增加如下XML片断Q红色部分)Q?/p>
<XDtMethod:forAllMethods superclasses="true">
<XDtMethod:ifHasMethodTag tagName="spring.property">
<property name="<XDtMethod:propertyName/>">
<XDtMethod:ifHasMethodTag tagName="spring.property" paramName="value">
<value><XDtMethod:methodTagValue tagName="spring.property" paramName="value"/></value>
</XDtMethod:ifHasMethodTag>
<XDtMethod:ifHasMethodTag tagName="spring.property" paramName="ref">
<ref bean="<XDtMethod:methodTagValue tagName="spring.property" paramName="ref"/>"/>
</XDtMethod:ifHasMethodTag>
<XDtMethod:ifHasMethodTag tagName="spring.property" paramName="list">
<list>
<XDtMethod:forAllMethodTagTokens tagName="spring.property" paramName="list">
<value><XDtMethod:currentToken/></value>
</XDtMethod:forAllMethodTagTokens>
</list>
</XDtMethod:ifHasMethodTag>
<XDtMethod:ifHasMethodTag tagName="spring.property" paramName="list.ref">
<list>
<XDtMethod:forAllMethodTagTokens tagName="spring.property" paramName="list.ref">
<ref bean="<XDtMethod:currentToken/>"/>
</XDtMethod:forAllMethodTagTokens>
</list>
</XDtMethod:ifHasMethodTag>
</property>
</XDtMethod:ifHasMethodTag>
</XDtMethod:forAllMethods>
注意U色部分的代码,我们仿照listQ增加一个list.ref来实现引用类型的List?br />现在Q修Ҏ释如下:
/**
* @spring.property list.ref="articleHandler,imageHandler"
*/
public void setHandlers(List handlers) {
...
}
备䆾好原有的xdoclet-spring-module-1.2.3.jarQ然后将修改后的目录打包Q?/p>
jar cvf xdoclet-spring-module-1.2.3.jar .
<property name="handlers">
<list>
<ref bean="articleHandler"/>
<ref bean="imageHandler"/>
</list>
</property>
cM的,我们q可以增加XDoclet对Map注入的支持?/p>
发现多功能的模板驱动的代码生成器
![]() |
U别: 初
Sing Li
, 作? Wrox Press 2004 q?11 ?05 ?/p> 开放源代码?XDoclet 代码生成引擎Q是许多领先?Java 框架不可~少的组成部分,常常被用作面向属性的~程和持l集成的引擎。但?XDoclet q有一些不太惹人注目的地方Q对初开发h员来_它太难掌握、太隄通。在q篇文章中,行作?Sing Li ?XDoclet 为对象,揭示了其内部单却优雅的设计,使您能够理解q项技术,q将它应用在实践当中?/blockquote> |
如图 3 所C,Apache Ant 在运行的时候控制着 XDoclet 的配|和操作。XDoclet 解析输入?Java 源代码,q在内存中生成结构模型。模板引擎通过处理一l模板和标签处理器,生成输出文g。模板和标签处理器可以是内置的,也可以是定制的。在代码生成期间Q模板和标签处理器拥有对l构模型的完全访问?
XDoclet 实质上就是一个通用?Javadoc 引擎Q请参阅侧栏Q?通用?Javadoc 引擎Q。那么,是什么让它看hq么复杂呢?{案在于QXDoclet 几乎从未被单独讨Q而L藏在其他许多复杂的技术中。图 4 昄了了围绕?XDoclet 周围的复杂性迷雾(请参阅侧?Z?XDoclet 看v来比实际的要复杂得多Q?
![]() |
|
在图 4 中,您可以看?XDoclet 与以下内Ҏ紧密相关的:
XDoclet 本n却是惊h地简单,正如下面CZ中的工作代码所C的那样?/p>
![]() ![]() |
![]()
|
现在Q您可以通过研究我向您提供的数据入口应用E序CZQ来观察 XDoclet 的实际工作。(要下载这个示例中使用?Java 代码、XDoclet 模板?Ant 脚本Q请单击本文剙或底部的 Code图标Q或者请参阅 下蝲部分。)我们从查清?1 所C的 Java 代码开始,q部分代码表CZ一个客L地址。该地址被编码成 JavaBean lgQ其中的 XDoclet 标签是以黑体字Ş式显C的Q?
|
在清?1 中,需要注意的是,要把 XDoclet 标签嵌入到注释中Q紧攑֜相关代码元素Q例如字Dc方法、接口或c)的前面。在解析源代码时QXDoclet 会ؓ每个标签建立一个属性,q将该属性附加到l构模型的代码元素上。现在,h?@dw.genStruts
标签Q因是在本例中将用到的第一个模ѝ?
对于本例Q您需要生成新?Java cȝ代码 —?一?Struts 表单 bean。Struts 会用q个 bean 保存q传输用戯入。bean 必须?bean 属性的形式包含所有数据字D,而且它必L org.apache.struts.action.ActionForm
的子cR?
Z生成表单 bean 的代码,您要Ҏ清单 2 所C的伪代码生?XDoclet 模板。括号中的黑体字代表控制逻辑和您要进行替换的文本。请注意模板是如何从已解析的 Java 源代码文件的l构模型中提取信息的Q?
|
![]() |
|
在清?2 的@环中的代码生成了一个字D声明和一个访问器ҎQ还入源代码中每个用 @dw.genStruts
标记的访问器Ҏ生成了一个设|器Ҏ?
清单 2 使用了易于理解的伪代码来表示模板替换标签。实际的 XDoclet 模板标签则相当繁琐。清?3 昄?genformbean.xdt 模板Q所有的 XDoclet 模板都保存在 .xdt 文g中)。我已经用黑体字?XDoclet 模板标签Q以方便在伪代码中对其进行引用?
|
![]() |
|
您可以参?XDoclet 的“模板语a”文,查找 XDoclet 所有可用标{列表Q请参阅 参考资?/font>Q?
要运行用?AddressBean.java 源文件的模板Q请使用以下 Ant 命o行:
|
q个命o可以执行定制 Ant 目标Q请参阅侧栏 熟悉 Ant 脚本~写Q来处理 genbeanform.xdt 模板。XDoclet 提供?Ant d叫作 xdoclet.DocletTask
Q它被用来运行模板文件。如果您?Ant 的细节感兴趣Q请参阅CZ代码中的 build.xml 文gQ以了解更多信息?
?XDoclet 处理模板的时候,它在名ؓ generated的子目录下生成一?AddressBeanForm.java 文g。清?4 昄了该文gQ它包含模板处理期间替换的所有文本:
|
您可以用相同?AddressBean.java 源文Ӟ但是?genformjsp.xdt 模板生成数据入口表单 JSP 面。清?5 昄?genformjsp.xdtQ?
|
h意,代码中用 <XDt:methodTagValue>
取得原始 AddressBean.java 代码中在 XDoclet 标签中指定的属性倹{?
当您执行 genstruts
Ant 目标的时候,也会处理清单 5 昄?genformjsp.xdt 模板。您可以?generated 子目录中扑ֈ生成?AddressBeanForm.jsp 文gQ检查文件内容,可以看到要对模板q行的替换?
![]() |
|
您可以用 XDoclet 生成LZ文本的输出。我向您展示的例子?XDoclet 生成?Java 代码、JSP 面、XML 文g、配|文件以及其他更多输出。它从一个简单的?XDoclet q行标记?Java 源文?AddressBean.javaQ徏立了一个完整的数据入口 Web 应用E序。ؓ了做到这一点,它执行了 XDoclet 的内|模板(位于 JAR 文g中,UCؓ模块Q,从而生成:
?1 昄了ؓCZ应用E序生成的所有文Ӟ通常UCؓ 工gQartifactQ?/em>Q:
生成的工?/strong> | 说明 | 位置 |
AddressBeanForm.java | Java 源文Ӟ包含表单 bean c,?Struts 的表单处理中使用 | generated目录 |
AddressBeanForm.jsp | JSP 表单Q用 Struts 标签库接受用户地址输入 | jsp目录 |
AddressBeanAction.java | Struts 动作c,接受输入|?Hibernate 把g存到关系数据?/td> | generated目录 |
AddressBean.hbm.xml | Hibernate 映射文gQ在 AddressBean Java 对象和数据库的关pd ADDRESS 表之间进行映? | web/classes目录 |
dwschema.sql | RDBMS 表的架构Q用来对 AddressBean 对象的实例进行持久化 | sql目录 |
hibernate.cfg.xml | Hibernate q行时的配置文g | web/classes目录 |
web.xml | 生成?Web 应用E序的部|描q符 | web目录 |
struts-config.xml | Struts 框架的配|文?/td> | web目录 |
在这文章中Q您详细了解了表 1 中所列的两个工g中的W一个工件的生成Q深入了解了生成它们的模ѝ工?AdddressBeanAction.java 则用cM的方法,利用叫作 genaction.xdt 的模板生成。XDoclet h内置模板和标{֤理器Q可以生成表 1 中的其他工g?/p>
?2 列出了每个生成的工g对应?Ant 目标?Ant d。您可以执行表格中的每个 Ant 目标Q生成对应的工g。所有这些生成的工gQ再加上原始?AddressBean.javaQ共同构成了CZ Web 应用E序。您q会发现叫作 all
的默?Ant 目标Q它会ؓ您做M事,包括为应用程序徏?WARQ可以部|的 Web 归Q。在q行处理之前Q一定要阅读代码发布包中?README.txt 文g?
![]() |
|
Ant 目标 | Ant d | 工g |
genstruts | xdoclet.DocletTask | AddressBeanForm.java |
genstruts | xdoclet.DocletTask | AddressBeanForm.jsp |
genstruts | xdoclet.DocletTask | AddressBeanAction.java |
generateHIB | xdoclet.modules.hibernate.HibernateDocletTask | AddressBean.hbm.xml |
generateHIB | xdoclet.modules.hibernate.HibernateDocletTask | hibernate.cfg.xml |
createDDL | xdoclet.modules.hibernate.HibernateDocletTask | dwschema.sql |
generateDD | xdoclet.modules.web.WebDocletTask | web.xml |
generateDD | xdoclet.modules.web.WebDocletTask | struts-config.xml |
![]() ![]() |
![]()
|
XDoclet 是一个有用的、智能的代码生成器,您可以用它自动进行许多日常的 Java 开发Q务。不要被它表面的复杂所吓退。随着逐渐_N?XDocletQ以及与之相关的 Apache AntQ,您会节约您宝늚旉Qƈ在未来的开发工作中Q得到数倍的回报?
![]() ![]() |
![]()
|
名字 | 大小 | 下蝲Ҏ |
---|---|---|
j-xdoclet-code.zip | FTP |
![]() | ||||
![]() | 关于下蝲Ҏ的信?/font> | ![]() | ![]() | Get Adobe] Reader] |
![]() ![]() |
![]()
|
![]() ![]() |
![]()
|
![]() | ||
![]() | Sing Li ?Professional Apache Tomcat 5?Pro JSP, Third Edition?Early Adopter JXTA?Professional JiniQ以?Wrox Press 出版的许多其他图书的作者。他是技术杂志的定期撰稿人,q是 P2P 发展的热心传播者。Sing 是一名咨询顾问和资深作者,您可以通过他的电子邮g westmakaha@yahoo.com与他联系? |
您可以看刎ͼ包含嵌入?XDoclet 标签?Java 源代码是pȝ的输入。在 Apache Ant 的驱动下QXDoclet 处理输入的代码,生成的输出文本文件可以是 Java 源代码、HTML 面、XML 文g{。ؓ了处理输入,XDoclet 需要用模板(保存?.xdt 文g中)和标{֤理器Q用 Java ~码Q。XDoclet 把模板和标签处理器打包成“模块”,不同的“模块”处理不同的问题域?/p>
XDoclet 生成的结构模?
XDoclet 对包含嵌入式 XDoclet 标签的输?Java 源代码进行解析,qؓ代码建立非常详细的结构模型。结构模型中的每个元素都代表源代码中的一?Java l构。图 2 昄的结构模型,揭示?XDoclet 跟踪的代码构造和关系?
?2. XDoclet 的解析的 Java 源代码的内部l构模型
?2 中的l构模型跟踪cR接口、方法之cȝ代码构造(模型元素Q。该模型q跟t元素之间的关系Q例如承和接口实现。以内联注释的Ş式嵌入在源代码中?XDoclet 标签被解析ؓ模型元素的属性,q被跟踪
深入 XDoclet
?3 昄?XDoclet 的内部结构,揭示了其运行的功能块?
如图 3 所C,Apache Ant 在运行的时候控制着 XDoclet 的配|和操作。XDoclet 解析输入?Java 源代码,q在内存中生成结构模型。模板引擎通过处理一l模板和标签处理器,生成输出文g。模板和标签处理器可以是内置的,也可以是定制的。在代码生成期间Q模板和标签处理器拥有对l构模型的完全访问?
XDoclet 虚假的复杂?/span>
XDoclet 实质上就是一个通用?Javadoc 引擎Q请参阅侧栏Q?a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">通用?Javadoc 引擎Q。那么,是什么让它看hq么复杂呢?{案在于QXDoclet 几乎从未被单独讨Q而L藏在其他许多复杂的技术中。图 4 昄了了围绕?XDoclet 周围的复杂性迷雾(请参阅侧?a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Z?XDoclet 看v来比实际的要复杂得多Q?
Z?XDoclet 看v来比实际的要复杂得多
XDoclet 处理的问题领域是复杂性的另一个来源。在发布 XDoclet 的时候,XDoclet 已经可以?EJB lg集成、J2EE Web 容器集成、Hibernate 持久性层、Struts 框架、Java 理扩展QJMXQ等生成代码。这些问题领域中的每一个领域都有一大套该领域专用的行话和概c从q些复杂的问题领域出来的问题Q经怸导着 XDoclet 的讨论,q也提高?XDoclet 表面的复杂性。可能是“只见森林,不见树木”? |
在图 4 中,您可以看?XDoclet 与以下内Ҏ紧密相关的: