如何使用XDoclet来简化EJB的开发
根据EJB的规范,有些代码可以从Bean代码推导出来,如Home,Remote接口代码,而类似于ejb-jar.xml这样的Deployment Descriptor配置文件,如果让人手工输入也是一件很烦人的事情。XDoclet恰好能够解决这样的问题,XDoclet是一个开源的code generation engine. 它可以在JAVA中采用面向属性的编程。简单通俗的讲,你在JAVA代码中加入一些元数据,类似于JavaDoc标签,XDoclet就可以解析这样的源文件,然后根据源文件和元数据来产生指定的代码文件、XML文件。特别对于EJB,你可以只需要维护单个EJB源文件,其他文件如HOME,REMOTE都由XDoclet自动产生,这可以极大的减少你的开发时间。
XDoclet可以作为ANT的一个编译部分来被调用,这样,由ANT在编译之前先调用XDoclet自动产生所需的文件,然后再编译,打包,部署。下面我以stateless session bean作为示范例子,XDoclet目前版本只支持到EJB 2.0,该例子代码规范遵从EJB 2.0,环境和工具如下:
JRE: 1.6.0_07 http://www.java.com/en/download/manual.jsp
开发工具: Eclipse v3.5.0 http://download.eclipse.org/eclipse/downloads/
编译部署: ANT v1.7.1 http://ant.apache.org/bindownload.cgi
EJB服务器: jboss-4.2.3.GA http://www.jboss.com/
XDoclet v1.2.3 http://xdoclet.sourceforge.net/xdoclet/install.html
(1)在Eclipse中创建一个新项目Calculator
(2)创建一个sessionBean -- CalculatorBean.java
package org.jboss.tutorial.stateless.bean;
import javax.ejb.SessionBean;
/**
*
*
* @ejb.bean name="Calculator" type="Stateless" view-type="remote"
* jndi-name="CalculatorBean/re"
*
* @ejb.transaction-type type="Container"
*
* @ejb.transaction type="Required"
*
*/
public abstract class CalculatorBean implements SessionBean
{
/**
* @ejb.interface-method
*/
public int add(int x, int y)
{
return x + y;
}
/**
* @ejb.interface-method
*/
public int subtract(int x, int y)
{
return x - y;
}
}
注:在类定义的注释中有@ejb.bean ,@ejb.transaction-type 之类的元数据,在方法前的注释中有@ejb.interface-method,这些都是XDoclet中定义的元数据,具体的作用可以参考XDoclet的文档,这里了解EJB的朋友应该不难从单词意义上去理解它。
(3)创建用于ANT的build.xml文件
注意:脚本里有个 env.JBOSS_HOME变量,这要求你必须在环境变量里加入一个JBOSS_HOME,它的值指向jboss的安装目录。
请注意观察红色字体部分的脚本,在执行compile目标之前,将调用XDoclet, 所以在这个文件里必须指定XDoclet的安装目录。如果你
在自己的电脑上调试时,要注意XDoclet的安装目录是否正确。在这个例子代码中,XDoclet会在目标目录中产生HOME,REMOTE,SessionBean,Deployment Descriptor等文件,包括针对jboss的deploy文件。
<?xml version="1.0"?>
<!-- ======================================================================= -->
<!-- JBoss build file -->
<!-- ======================================================================= -->
<project name="JBoss" default="ejbjar" basedir=".">
<property environment="env"/>
<property name="src.dir" value="${basedir}/src"/>
<property name="jboss.home" value="${env.JBOSS_HOME}"/>
<property name="jboss.server.config" value="default"/>
<property name="build.dir" value="${basedir}/build"/>
<property name="build.classes.dir" value="${build.dir}/classes"/>
<property name="build.src.dir" value="${build.dir}/src"/>
<property name="build.artifact" value="jboss-ejb3-tutorial-stateless.jar"/>
<!-- Build classpath -->
<path id="classpath">
<!-- So that we can get jndi.properties for InitialContext -->
<pathelement location="${basedir}"/>
<!-- Only the jbossall-client.jar should ideally be sufficient -->
<fileset dir="${jboss.home}/client">
<include name="**/jbossall-client.jar"/>
</fileset>
<!-- javax.persistence.* -->
<fileset dir="${jboss.home}/server/default/lib">
<include name="ejb3-persistence.jar"/>
<include name="jboss-ejb3x.jar" />
<include name="log4j.jar" />
</fileset>
<pathelement location="${build.classes.dir}"/>
</path>
<property name="build.classpath" refid="classpath"/>
<!-- Override with your XDoclet bundle dist location -->
<property name="xdoclet.home" value="E:/Program Files/java-sdk/xdoclet-1.2.3"/>
<property name="xdoclet.lib" value="${xdoclet.home}/lib"/>
<path id="xdoclet.path">
<fileset dir="${xdoclet.lib}">
<include name="*.jar"/>
</fileset>
<path refid="classpath"/>
</path>
<!-- =================================================================== -->
<!-- Prepares the build directory -->
<!-- =================================================================== -->
<target name="prepare">
<mkdir dir="${build.dir}"/>
<mkdir dir="${build.classes.dir}"/>
<mkdir dir="${build.src.dir}"/>
</target>
<!-- =================================================================== -->
<!-- Compiles the source code -->
<!-- =================================================================== -->
<target name="compile" depends="prepare,ejbdoclet">
<javac
destdir="${build.classes.dir}"
debug="on"
deprecation="on"
optimize="off"
includes="**">
<classpath refid="classpath"/>
<src path="${build.src.dir}" />
<src path="${src.dir}" />
</javac>
</target>
<!-- =================================================================== -->
<!-- Invoke XDoclet's ejbdoclet -->
<!-- =================================================================== -->
<target name="ejbdoclet" unless="xdoclet.skip">
<taskdef
name="ejbdoclet"
classname="xdoclet.modules.ejb.EjbDocletTask"
classpathref="xdoclet.path"
/>
<ejbdoclet
destdir="${build.src.dir}"
excludedtags="@version,@author,@todo,@since"
addedtags="@xdoclet-generated at ${TODAY},@copyright The XDoclet Team,@author XDoclet,@version ${version}"
ejbspec="2.0"
force="${xdoclet.force}"
verbose="false"
>
<fileset dir="${src.dir}">
<include name="**/*Bean.java"/>
</fileset>
<packageSubstitution packages="bean" substituteWith="interfaces"/>
<remoteinterface/>
<localinterface/>
<homeinterface/>
<localhomeinterface/>
<!--dataobject/-->
<!--valueobject/-->
<!--entitypk/-->
<entitycmp/>
<!--entitybmp/-->
<session/>
<!--dao>
<packageSubstitution packages="ejb" substituteWith="dao"/>
</dao-->
<!--utilobject cacheHomes="true" includeGUID="false"/-->
<deploymentdescriptor
destdir="${build.classes.dir}/META-INF"
validatexml="true"
description="stateless EJBs" >
</deploymentdescriptor>
<jboss
version="4.0"
unauthenticatedPrincipal="nobody"
xmlencoding="UTF-8"
destdir="${build.classes.dir}/META-INF"
validatexml="true"
/>
</ejbdoclet>
</target>
<target name="ejbjar" depends="compile">
<jar jarfile="build/${build.artifact}">
<fileset dir="${build.classes.dir}">
<include name="**/*.class"/>
<include name="**/*.*" />
</fileset>
</jar>
<copy file="build/${build.artifact}" todir="${jboss.home}/server/${jboss.server.config}/deploy"/>
</target>
<target name="run" depends="ejbjar">
<java classname="org.jboss.tutorial.stateless.client.Client" fork="yes" dir=".">
<classpath refid="classpath"/>
</java>
</target>
<!-- =================================================================== -->
<!-- Cleans up generated stuff -->
<!-- =================================================================== -->
<target name="clean.db">
<delete dir="${jboss.home}/server/${jboss.server.config}/data/hypersonic"/>
</target>
<target name="clean">
<delete dir="${build.dir}"/>
<delete file="${jboss.home}/server/${jboss.server.config}/deploy/${build.artifact}"/>
</target>
</project>
(4)创建Client测试代码 -- Client.java
package org.jboss.tutorial.stateless.client;
import org.jboss.tutorial.stateless.interfaces.*;
import javax.rmi.*;
import javax.naming.InitialContext;
public class Client
{
public static void main(String[] args) throws Exception
{
InitialContext ctx = new InitialContext();
Object ref = ctx.lookup("CalculatorBean/re");
CalculatorHome home = (CalculatorHome) PortableRemoteObject.narrow(ref,CalculatorHome.class);
Calculator calculator = home.create();
System.out.println("1 + 1 = " + calculator.add(1, 1));
System.out.println("1 - 1 = " + calculator.subtract(1, 1));
System.out.println("Hello Xdoclet");
}
}
(5) 在eclipse里,右键选择build.xml,run as->ant build,就会自行进行编译和部署,会产生一个jboss-ejb3-tutorial-stateless.jar
文件,并被拷贝到"jboss安装目录/server/default/deploy"目录下
(6)双击"jboss安装目录/bin/run.bat",运行jboss。在提示信息中可以看到该ejb被加载。
INFO [EjbModule] Deploying Calculator
INFO [ProxyFactory] Bound EJB Home 'Calculator' to jndi 'CalculatorBean/re'
(7)配置jndi,进入到"项目目录/build/classes"目录,创建一个jndi.properties文件:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost
(8)在"项目目录/build/classes"目录下,用命令行方式输入
java -cp .;C:/jboss-4.2.3.GA/client/jbossall-client.jar org.jboss.tutorial.stateless.client.Client
1 + 1 = 2
1 - 1 = 0
Hello xdoclet
总结: Xdoclet主要就是简化你的开发工作,把EJB当中许多代码给你自动产生出来,这样特别有利于开发人员维护代码,减少无用的开发工作,把精力专注于业务逻辑中。
参考: http://xdoclet.sourceforge.net/xdoclet/index.html
http://blog.csdn.net/omage/archive/2010/01/14/5191568.aspx