|
任何人都想過自動生成的代碼,自己去寫個分析器不是件容易的事,而XDoclet給我們機會建立自己的代碼生成器。
XDoclet最早用來對付EJB,因為生成EJB的本地和遠(yuǎn)程接口以及對應(yīng)的HOME和描述符是一件簡單但是枯燥的事,本來我以為,只有JBuilder這樣的集成編輯器可以很好的簡化工作,后來知道XDoclet也能夠完成得不錯。 我最早用XDoclet是因為學(xué)習(xí)hibernate,hibetnate的描述文件并不復(fù)雜,關(guān)鍵的部分不多,可是一旦對POJO修改,就必須要勞師動眾的找到對應(yīng)的描述符來修改,增加了一次出錯的機會,而使用了XDoclet就可以做到同步的修改。 還有就是struts,首先是配置文件,許多人操作同一個文件會產(chǎn)生沖突,有了XDoclet我們就不怕了,還有validate文件也一樣,有了XDoclet就會解決大部分沖突問題。 之所以起這么一個名字,主要因為XDoclet和Ant結(jié)合得很緊,盡管實際上做的工作并沒有直接的聯(lián)系,但XDoclet除了Ant接口就只有些Maven接口插件了,所以XDoclet幾乎是完全依賴Ant的。
本篇文章的目錄結(jié)構(gòu)如下,因為只是為了說明問題,在我的ant的build文件中并沒有包括路徑名的引用,一切是直接的方式。 ├─classes ├─doc ├─gen ├─lib │ commons-collections-2.1.jar │ commons-logging-1.0.3.jar │ commons-validator.jar │ log4j-1.2.8.jar │ servlet.jar │ struts.jar │ xdoclet-1.2.1.jar │ xdoclet-apache-module-1.2.1.jar │ xdoclet-ejb-module-1.2.1.jar │ xdoclet-hibernate-module-1.2.1.jar │ xdoclet-web-module-1.2.1.jar │ xdoclet-xdoclet-module-1.2.1.jar │ xjavadoc-1.0.3.jar ├─merge ├─src ├─todo └─web
只列出lib中的文件,每一個的作用在后面慢慢描述。
build.xml 開頭增加 <path id="xdoclet.classpath"> <fileset dir="lib"> <include name="*.jar"/> </fileset> <pathelement location="classes"/> </path> **************************************************************************************************
1,最簡單的todolist
每 一篇講XDoclet都送這里開始,有很多原因的。XDoclet的靈感來自JavaDoc,JavaDoc把文檔寫在代碼里,緩解了困擾編程領(lǐng)域多年的 文檔與程序同步問題。這里有個很有趣的事,就是UNIX業(yè)界的人們傳遞下來這樣一個傳統(tǒng),就是代碼是最好的文檔,保持文檔的同步實在是費力不討好的事,所 以他們提出這樣一個好主意,不過JavaDoc更聰明,文檔是程序注釋的一部分,而且可以提取出來。
來吧,看這個任務(wù)。 <target name="todolist" description="todolist"> <taskdef name="documentdoclet" classname="xdoclet.modules.doc.DocumentDocletTask" classpathref="xdoclet.classpath"/> <documentdoclet destdir="todo" > <fileset dir="src"> <include name="**/*.java" /> </fileset> <info header="Todo list" projectname="XDoclet in Action" tag="todo"/> </documentdoclet> </target>
然后src寫這么一個文件 package xdoclet;
public class TodoListTest { /** * @todo 我有許多工作要做,只是測試,忽略吧 */ public TodoListTest() { }
/** * @todo 我還不知道名字 ,只是測試,忽略吧 * */ public String getYourName(){ return null; } }
注意要按照javadoc的寫法。還要注意ant中的子任務(wù)系統(tǒng),其中info就是我們定義的documentdoclet任務(wù)的子任務(wù),我們以后會看到很多類似的情況. 然后運行ant todolist 結(jié)果就是一個結(jié)構(gòu)類似javadoc,但是只包括todo標(biāo)簽的html文檔,呵呵,可以看看項目里有哪些待辦的事。
2,web.xml和taglib 作servlet映射是個討厭的工作,當(dāng)你接收別的項目的時候,這個項目的servlet怎么用可能比較麻煩,可能當(dāng)時web.xml的映射找不到了,這時怎么辦呢?
看這個文件 package com.xdocletbook.blog.servlet;
import java.io.IOException; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Level; import org.apache.log4j.Logger;
/** * @web.servlet * name="HomePage" * @web.servlet-init-param * name="LogLevel" * value="${LOG_LEVEL}" * @web.servlet-mapping * url-pattern="/home" * * @web.security-role * role-name="${OwnerRole}" * @web.security-role-ref * role-name="blogowner" * role-link="${OwnerRole}" */
public class HomePageServlet extends HttpServlet { private static Logger LOGGER = Logger.getLogger(HomePageServlet.class); public void init() throws ServletException { String logLevel = getInitParameter("LogLevel"); if (logLevel != null && logLevel.length() > 0) { LOGGER.setLevel(Level.toLevel(logLevel)); } }
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { LOGGER.debug("Displaying home page"); request.getRequestDispatcher("jsp/home.jsp").forward(request, response); } } 然后配置build.xml增加如下任務(wù)
<target name="generate-web"> <taskdef name="webdoclet" classname="xdoclet.modules.web.WebDocletTask" classpathref="xdoclet.classpath"/> <!-- Generate servlet and JSP Tag "stuff" --> <webdoclet destdir="gen" mergeDir="merge"> <fileset dir="src"> <include name="**/*.java" /> </fileset> <deploymentdescriptor destdir="web" distributable="false" /> </webdoclet> </target>
然后運行ant generate-web
結(jié)果就是這樣一個web.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app >
<!-- To use non XDoclet filters, create a filters.xml file that contains the additional filters (eg Sitemesh) and place it in your project‘s merge dir. Don‘t include filter-mappings in this file, include them in a file called filter-mappings.xml and put that in the same directory. -->
<!-- To use non XDoclet filter-mappings, create a filter-mappings.xml file that contains the additional filter-mappings and place it in your project‘s merge dir. -->
<!-- To use non XDoclet listeners, create a listeners.xml file that contains the additional listeners and place it in your project‘s merge dir. -->
<servlet> <servlet-name>StrutsActionServlet</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> </servlet>
<servlet> <servlet-name>HomePage</servlet-name> <servlet-class>com.xdocletbook.blog.servlet.HomePageServlet</servlet-class>
<init-param> <param-name>LogLevel</param-name> <param-value>1</param-value> </init-param>
<security-role-ref> <role-name>blogowner</role-name> <role-link>aOwner</role-link> </security-role-ref> </servlet>
<servlet-mapping> <servlet-name>StrutsActionServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
<servlet-mapping> <servlet-name>HomePage</servlet-name> <url-pattern>/home</url-pattern> </servlet-mapping>
<!-- To specify mime mappings, create a file named mime-mappings.xml, put it in your project‘s mergedir. Organize mime-mappings.xml following this DTD slice:
<!ELEMENT mime-mapping (extension, mime-type)> -->
<!-- To specify error pages, create a file named error-pages.xml, put it in your project‘s mergedir. Organize error-pages.xml following this DTD slice:
<!ELEMENT error-page ((error-code | exception-type), location)> -->
<!-- To add taglibs by xml, create a file called taglibs.xml and place it in your merge dir. -->
<!-- To set up security settings for your web app, create a file named web-security.xml, put it in your project‘s mergedir. Organize web-security.xml following this DTD slice:
<!ELEMENT security-constraint (display-name?, web-resource-collection+, auth-constraint?, user-data-constraint?)> <!ELEMENT web-resource-collection (web-resource-name, description?, url-pattern*, http-method*)> <!ELEMENT web-resource-name (#PCDATA)> <!ELEMENT url-pattern (#PCDATA)> <!ELEMENT http-method (#PCDATA)> <!ELEMENT user-data-constraint (description?, transport-guarantee)> <!ELEMENT transport-guarantee (#PCDATA)>
<!ELEMENT login-config (auth-method?, realm-name?, form-login-config?)> <!ELEMENT auth-method (#PCDATA)> <!ELEMENT realm-name (#PCDATA)> <!ELEMENT form-login-config (form-login-page, form-error-page)> <!ELEMENT form-login-page (#PCDATA)> <!ELEMENT form-error-page (#PCDATA)> -->
<security-role> <role-name>aOwner</role-name> </security-role>
</web-app>
仔 細(xì)看這個文件,你一定詫異struts的配置信息怎樣得來,這是XDoclet的另一種方式,對于第三方的Servlet,我們沒有辦法再處理原代碼,所 以我們有了merge選項,看<webdoclet destdir="gen" mergeDir="merge">這一句就知道了,在merge目錄里我們有兩個文件: servlets.xml
<servlet> <servlet-name>StrutsActionServlet</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> </servlet> servlet-mappings.xml
<servlet-mapping> <servlet-name>StrutsActionServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> 還要注意的是${LOG_LEVEL},這個是說要引用ant 中的變量,所以我的build.xml前面中增加了這兩項 <property name="LOG_LEVEL" value="1"/> <property name="OwnerRole" value="aOwner"/>
所以,我們就可以動態(tài)的改變部署的Log級別
3,struts自動配置
Struts中有兩樣比較重要的類,Action和Form。 對于Action,我們需要配置Action的映射和Forward屬性,對于Form我們也需要注冊名字和校驗參數(shù),以下就是我們能用XDoclet做到的。 對于Action我們寫這樣一個Java文件
package com.xdocletbook.blog.servlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping;
/** * Simple class to test Jakarta Struts generation (Jakarta Struts 1.2 beta 2 only). * * @struts.action * path="/struts/foo" * name="userForm" * input="jsp/createBlog.jsp" * * @struts.action-forward * name="success" * path="/struts/getAll.do" * redirect="false" * * * @struts.action-exception * type="com.xdocletbook.blog.exception.ApplicationException" * key="app.exception" * path="jsp/error.jsp" * * @struts.action-form name="blog.Create" */ public final class StrutsAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { return mapping.findForward("success"); } }
關(guān)鍵部分就是注釋部分。
看我們增加build.xml一個任務(wù), <target name="generate-web"> <taskdef name="webdoclet" classname="xdoclet.modules.web.WebDocletTask" classpathref="xdoclet.classpath"/> <!-- Generate servlet and JSP Tag "stuff" --> <webdoclet destdir="gen" mergeDir="merge"> <fileset dir="src"> <include name="**/*.java" /> </fileset> <strutsconfigxml version="1.1"/> </webdoclet> </target>
運行ant generate-web,我們就在gen得到了struts-config.xml 其中關(guān)鍵內(nèi)容如下 <action path="/struts/foo" type="com.xdocletbook.blog.servlet.StrutsAction" name="userForm"
scope="request" input="jsp/createBlog.jsp" unknown="false" validate="true" > <exception
key="app.exception" type="com.xdocletbook.blog.exception.ApplicationException" path="jsp/error.jsp" />
<forward name="success" path="/struts/getAll.do" redirect="false" /> </action>
如果我們有許多Action,就可以隨時生成這樣一個文件,不必在意有人改過這個文件。同時你也不必?fù)?dān)心不小心忘了改這個文件,因為你改了
Java時,許多默認(rèn)的屬性也跟這改了。
看到這里,許多用過workshop的一定感覺到頁面流不就是這樣嗎?當(dāng)通過圖形界面定義流程時,看看頁面流的源碼你就會發(fā)現(xiàn),注釋中有一些
特殊的標(biāo)記,這說明workshop的注釋有著xdoclet一樣的功能,只不過workshop提供了很好的界面,而不需要自己寫注釋,而且workshop提供了
更好的語法檢查,呵呵,只是將許多action寫到一起,是有些亂。
Struts的另一個主要的部分就是Form了,雖然我一開始覺得Form有些麻煩,對付動態(tài)的Form有些無能為力,但是結(jié)合一些相關(guān)的插件后,效果
確實不錯。
這是我們的Form文件,我們還使用ValidatorForm來做自動驗證。
package com.xdocletbook.blog.servlet;
import java.io.Serializable; import org.apache.struts.validator.ValidatorForm;
/** * * @struts.form * name="blog.Create" */ public class BlogCreateForm extends ValidatorForm implements Serializable { private String name; private String owner; private String email; public BlogCreateForm() {}
public String getName() { return this.name; } /** * @struts.validator * type="required" */
public void setName(String name) { this.name = name; }
public String getOwner() { return this.owner; } /** * @struts.validator * type="required" */
public void setOwner(String owner) { this.owner = owner; }
public String getEmail() { return this.email; } /** * @struts.validator * type="required" * @struts.validator * type="email" */
public void setEmail(String email) { this.email = email; } }
然后運行ant generate-web
這樣struts-config.xml就有了 <form-beans> <form-bean name="blog.Create" type="com.xdocletbook.blog.servlet.BlogCreateForm" />
<!
-- If you have non XDoclet forms, define them in a file called struts-forms.xml and place it in your merge
directory. --> </form-beans>
這里有一個陷阱,就是Struts XDoclet處理form-beans時,只處理類型是Form的,對于類型是ValidatorForm的Form如果你不把對應(yīng)的類文件放
到classpath下,XDoclet就會忽略它,所以struts的包一定要放到類路徑下,讓XDoclet有機會知道ValidatorForm是Form的子類。
還有每一個setXXX方法,有一些表示限制的注釋,這些幫助我們生成了
validation.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons
Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">
<form-validation> <!--
Define global validation config in validation-global.xml --> <formset> <form name="blog.Create">
<field property="name" depends="required">
<arg0 key="blog.Create.name"/>
</field> <field property="owner" depends="required">
<arg0
key="blog.Create.owner"/> </field> <field property="email"
depends="required,email">
<arg0 key="blog.Create.email"/> </field> </form>
</formset> </form-validation>
不錯吧。 |