使用XDoclet,你能够在J2EE环境下更加高效地工作,你所看到的Bean以及Bean之间的关系将更加简单,许多繁杂的事情将远离你的EJB开发过程。
XDoclet从Rickard Oberg创建的EJBDoclet工具发展而来,它的设想很简单:避免为每个EJB提供多个文件,而是从单一Bean类文件中提供组件需要的所有信息。那么,这是如何实现的呢?Java没有.NET吹嘘的“属性”,但Java有Javadoc标记。我们可以把一个特殊的@标记放入Javadoc注释,然后让一个Doclet工具处理这些标记。由工具为指定的Bean生成合适的XML描述器文件和接口文件。XDoclet建立在EJBDoclet思想的基础上,但适用范围不再局限于EJB。现在,我们已经可以用XDoclet生成Web服务、Web应用描述器,甚至还可以对它进行扩展,满足自己的特殊需要。
@标记有一个标准的格式,包含一个“名称空间”以及一个属于该名称空间的“标记名称”。标记的属性以“名字=值”的形式在标记中指定。下面是一个例子:
/**
* @namespace:tag name="value" name2="value2" ...
*/
当前可用的名称空间包括:
ejb 标准的EJB信息(非厂商私有的信息)
jboss 面向JBoss应用服务器的信息。
weblogic 面向BEA Weblogic应用服务器的信息。
webSphere 面向IBM WebSphere应用服务器的信息。
orion 面向Orion应用服务器(Oracle)的信息。
castor 为Castor框架生成映射信息。
mvcsoft 为MVCSoft EJB 2.0持久化管理器生成文件。
soap 生成SOAP描述器。
struts 生成struts-config.xml。
web 为Web应用生成web.xml配置文件。
jsp 生成标记库扩展描述器信息。
从上面的清单可以看出,除了EJB之外,XDoclet还提供了许多其它方面的支持(因此它的名字也从EJBDoclet变成了XDcolet)。
以下本文使用eclipse结合myeclipse插件,进行ejb的开发和演示。安装好eclipse和myeclipse以后,就可以新建项目进行ejb project的开发。
1. 新建立EJB Project。
输入工程名称:myEJB,注意,src是默认的源码输出文件夹,因为在myeclipse中很多xdoclet配置文件的输出文件目标路径默认值都是该文件夹,所以建议大家不要修改这个默认值。
新建立项目后,由于目前没有ejb-jar.xml的配置文件,所以会有一个warning信息:
这个警告信息会在xdoclet运行生成ejb-jar.xml文件后消失。
2. 鼠标右键选择新建的myejb项目,弹出窗口中选择properties
进入到myeclipse-xdoclet,准备进行项目的xdoclet配置
增加一个标准的配置,然后选择ejb的标准配置
这个操作将生成ejb代码的选项都进行默认的配置,不依赖于任何的应用程序服务器
不过我这个演示是用的jboss的服务器,所以还需要增加jboss的选项,右键点上图中的ejbdoclet的根节点,选择add来增加ejb的生成项目,在出现的选择列表中,选择jboss
然后配置新增加的jboss的选项,一般来说,有如下4项需要填写,见下图标记部分:
其中,
version表明你用的jboss的版本,我用的是jboss-4.0.3,但是只需要标记4.0即可,如果你使用的是3.2.*的版本,那就填写成为3.2,
第二个是你在jboss中配置的datasource的名称
第三个是你用到的数据源映射名称,这个名称是不能够任意填写,必须按照jboss配置文件中填写,配置文件在C:jboss-4.0.3serverdefaultconfstandardjbosscmp-jdbc.xml(我的jboss安装目录)我测试用的是mysql,所以填写mySQL。如果是oracle9i的数据库,则为Oracle9i,更加详细的信息,察看standardjbosscmp-jdbc.xml文件
最后一个是生成jboss.xml和jbosscmp-jdbc.xml的目标文件夹,一般生成到src/META-INF文件夹。
到这里,xdoclet的配置完成,下面将创建EntityBean的类并根据定义的tag来生成代码。
首先创建一个entitybean,名称为User,如下图所示,需要注意,包名必须最后是.ejb结尾,否则xdoclet不认
创建完成后,检查生成的代码,User.java,会发现在类的上方,会出现如下的tag:
* @ejb.bean name="User"
* display-name="Name for User"
* description="Description for User"
* jndi-name="ejb/User"
* type="CMP"
* cmp-version="2.x"
* view-type="both"
这些是默认生成的tag,不能完全的符合我们的要求,我们修改成为如下,红色为增加的部分:
* @ejb.bean name = "User"
* type = "CMP"
* cmp-version = "2.x"
* display-name = "User"
* description = "User"
* view-type = "both"
* jndi-name = "ejb/UserHome"
* local-jndi-name = "ejb/UserLocalHome"
* primkey-field = "userId"
* @ejb.persistence table-name = "systemuser"
* @jboss.persistence table-name = "systemuser"
* @ejb:util
* generate="physical"
然后,需要为user entity增加它的几个相关的方法
在相关的地方修改这个抽象的类,修改后,红色为增加的部分:
public abstract class User implements EntityBean {
/** The entity context */
private EntityContext context;
/**
* @ejb.interface-method view-type = "both"
* @ejb.persistence column-name = "userId"
* @ejb.pk-field
*
* @return
*/
public abstract String getUserId();
/**
* @ejb.interface-method view-type = "both"
*
* @param userId
*/
public abstract void setUserId(String userId);
/**
* @ejb.interface-method view-type = "both"
* @ejb.persistence column-name = "userName"
*
* @return
*/
public abstract String getUserName();
/**
* @ejb.interface-method view-type = "both"
*
* @param userName
*/
public abstract void setUserName(String userName);
/**
* @ejb.interface-method view-type = "both"
* @ejb.persistence column-name = "Password"
*
* @return
*/
public abstract String getPassword();
/**
* @ejb.interface-method view-type = "both"
*
* @param password
*/
public abstract void setPassword(String Password);
好了,entitybean修改成为这样以后就可以生成相关的接口和实现类了
如果运行正常,在控制台窗口中会出现如下提示:
Buildfile: F:workspaceMyEJB.xdoclet-build.tmp.xml
N65540:
[ejbdoclet] (XDocletMain.start 47 ) Running <remoteinterface/>
[ejbdoclet] Generating Remote interface for 'com.mycom.myejb.entity.ejb.User'.
[ejbdoclet] (XDocletMain.start 47 ) Running <localinterface/>
[ejbdoclet] Generating Local interface for 'com.mycom.myejb.entity.ejb.User'.
[ejbdoclet] (XDocletMain.start 47 ) Running <homeinterface/>
[ejbdoclet] Generating Home interface for 'com.mycom.myejb.entity.ejb.User'.
[ejbdoclet] (XDocletMain.start 47 ) Running <localhomeinterface/>
[ejbdoclet] Generating Local Home interface for 'com.mycom.myejb.entity.ejb.User'.
[ejbdoclet] (XDocletMain.start 47 ) Running <dataobject/>
[ejbdoclet] Generating Data Object class for 'com.mycom.myejb.entity.ejb.User'.
[ejbdoclet] (XDocletMain.start 47 ) Running <valueobject/>
[ejbdoclet] (XDocletMain.start 47 ) Running <entitypk/>
[ejbdoclet] (XDocletMain.start 47 ) Running <entitycmp/>
[ejbdoclet] Generating CMP class for 'com.mycom.myejb.entity.ejb.User'.
[ejbdoclet] (XDocletMain.start 47 ) Running <entitybmp/>
[ejbdoclet] (XDocletMain.start 47 ) Running <session/>
[ejbdoclet] (XDocletMain.start 47 ) Running <dao/>
[ejbdoclet] (XDocletMain.start 47 ) Running <utilobject/>
[ejbdoclet] Generating Util class for 'com.mycom.myejb.entity.ejb.User'.
[ejbdoclet] (XDocletMain.start 47 ) Running <deploymentdescriptor/>
[ejbdoclet] Generating EJB deployment descriptor (ejb-jar.xml).
[ejbdoclet] (XDocletMain.start 47 ) Running <jboss/>
[ejbdoclet] Generating jboss.xml.
[ejbdoclet] Generating jbosscmp-jdbc.xml.
_xdoclet_generation_:
BUILD SUCCESSFUL
Total time: 8 seconds
如果你在运行中,出现了
N65540:
BUILD FAILED
java.lang.UnsupportedClassVersionError: xjavadoc/ant/XJavadocTask (Unsupported major.minor version 49.0)
的异常,建议你更换以下eclipse的jre环境。例如当我用jdk1.4.2的时候就出现上述异常,更换为jdk1.5后即正常运行。
修改生成的类为抽象的类
public abstract class UserManager implements SessionBean {
…………….
修改UserManager的xdoclet标签为:
* @ejb.bean name="UserManager"
* display-name="Name for UserManager"
* description="Description for UserManager"
* jndi-name="ejb/UserManagerHome"
* type="Stateless"
* view-type="both"
下面将增加一个login的方法,将UserManager.java移动到最下方,你会发现xdoclet已经给你创建了一个现有的方法:
/**
* An example business method
*
* @ejb.interface-method view-type = "both"
*
* @throws EJBException Thrown if method fails due to system-level error.
*/
public void replaceWithRealBusinessMethod() throws EJBException {
// rename and start putting your business logic here
}
根据你自己的需要修改这个方法,或者增加新的方法,不过一定要保留它的ejb的标签
例如增加一个方法:
/**
* @ejb.interface-method view-type = "both"
*
* @param username
* @param password
* @return
* @throws EJBException
*/
public boolean login(String username,String password) throws EJBException {
boolean loginresult = false;
if(username.equals("gary") && password.equals("gzllm")){
System.out.println("用户名与密码匹配,允许登录系统");
loginresult = true;
}else{
System.out.println("用户名与密码不匹配,不登录失败");
loginresult = false;
}
return loginresult;
}
然后再次运行xdoclet
运行成功后会生成如下图所示结构的代码:
这个时候就生成了最常用的SessionBean的代码。
1、 部署ejb代码,首先需要设置你的应用程序服务器,以下我配置了一个jboss的应用程序服务器。在eclipse的windows菜单下选择preferences,选择myeclipse选项,选择你需要使用的服务器,并配置相关的路径,如下图所示:
2、 在Jboss下面的进一步的选项中能够选择运行的模式,可以选择debug模式和run模式,建议现在选择debug模式
3、 JDK选用你正在使用的jdk,点ok配置结束
4、 由于发布的jboss的ejb程序需要用到mySQL数据库,所以还需要配置一下JBoss,首先拷贝mysql的数据库配置文件,从C:jboss-4.0.3docsexamplesjcamysql-ds.xml拷贝到需要发布的服务器中,修改配置文件数据库连接部分为:
<jndi-name>MyEJB</jndi-name>
<connection-url>jdbc:mysql://192.168.2.26:3306/MyEJB</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>root</password>
<connection-url>jdbc:mysql://192.168.2.26:3306/MyEJB</connection-url>然后启动你自己的mysql的数据库,并创建MyEJB数据库以及相关的User表。
另外,还需要将用到的数据库连接驱动程序mysql-connector-java-3.1.10-bin.jar(我用的mysql的jdbc驱动程序是mysql-connector-java-3.1.10)拷贝到应用程序服务器的lib目录中,我的目录是C:jboss-4.0.3serverdefaultlib,否则在启动jboss的时候会报异常:
16:48:25,078 WARN [JBossManagedConnectionPool] Throwable while attempting to get a new connection: null
org.jboss.resource.JBossResourceException: Could not create connection;
- nested throwable: (org.jboss.resource.JBossResourceException: Failed to register driver for: com.mysql.jdbc.Driver;
- nested throwable: (java.lang.ClassNotFoundException:
No ClassLoaders found for: com.mysql.jdbc.Driver))
5、 在eclipse的工具栏中找到如图所示的一个按钮
6、 增加一个配置
7、 选择刚才配置的jboss服务器
8、 选择完成。
9、 部署完成
10、 运行ejb,点击部署旁边的一个按钮:
11、 Jboss启动过程中,可以看到,刚才写的ejb部分已经发布成功。在控制台的启动log中,你可以看到类似以下语句:17:00:16,453 INFO [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:name=MyEJB,service=DataSourceBinding' to JNDI name 'java:MyEJB'
17:00:17,312 INFO [EjbModule] Deploying User
17:00:17,812 INFO [EjbModule] Deploying UserManager
17:00:18,250 INFO [BaseLocalProxyFactory] Bound EJB LocalHome 'User' to jndi 'ejb/UserLocalHome'
17:00:18,328 INFO [ProxyFactory] Bound EJB Home 'User' to jndi 'ejb/UserHome'
17:00:22,796 INFO [BaseLocalProxyFactory] Bound EJB LocalHome 'UserManager' to jndi 'UserManagerLocal'
17:00:22,812 INFO [ProxyFactory] Bound EJB Home 'UserManager' to jndi 'ejb/UserManagerHome'
17:00:22,828 INFO [EJBDeployer] Deployed: file:/C:/jboss-4.0.3/server/default/deploy/MyEJB.jar/
说明entitybean 与sessionbean 部署运行成功。
在JBoss的管理界面中,也可以找到如下的关于myejb.jar的部署说明:
1、 新建一个java project,project name 为MyEJBTest
在项目的libraries中,将J2EE的包添加到项目中,另外还需要增加一个变量,如下图所示:
然后将新增加的变量也增加到你的项目libories中:
2、 在项目的构建路径中增加对MyEJB项目的引用:
3、 新建一个类Test:
4、 编辑生成的Test的类,修改后的代码如下所示:
package com.mycompany.myejb.test;
import java.rmi.RemoteException;
import java.util.Properties;
import javax.ejb.CreateException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import com.mycom.myejb.session.interfaces.UserManager;
import com.mycom.myejb.session.interfaces.UserManagerHome;
public class Test {
Properties properties;
public Test() {
properties = new Properties();
properties.put("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs",
"org.jboss.naming:org.jnp.interfaces");
properties.put("java.naming.provider.url", "jnp://localhost:1099");
properties.put("jnp.disableDiscovery", "true");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t = new Test();
System.out.println("登录测试结果:" + t.testLogin("gary", "gzllm"));
System.out.println("登录测试结果:" + t.testLogin("errorUser", "gzllm"));
}
public boolean testLogin(String username, String password) {
boolean loginresult = false;
Context ctx;
try {
ctx = new InitialContext(properties);
Object object = ctx.lookup(UserManagerHome.JNDI_NAME);
UserManagerHome userManagerHome = (UserManagerHome) PortableRemoteObject
.narrow(object, UserManagerHome.class);
UserManager userManager = userManagerHome.create();
loginresult = userManager.login(username, password);
} catch (NamingException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (CreateException e) {
e.printStackTrace();
}
return loginresult;
}
}
5、 如上的测试程序测试部署成功的myejb.jar中的login的方法,如果输入的用户名与密码为gary/gzllm则返回true,否则其他的就返回false,运行生成的Test,可以看到如下输出结果:
整个程序测试成功。
1. 由于EJB的一个最基本的设计模式Session Façade,我并没有在测试程序中直接调用User这个entitybean,甚至为了简便,甚至生成了这个类以后就没有使用它。建议在SessionBean中再调用EntityBean,也就是说,你可以在UserManager这个sessionbean中调用User这个EntityBean进行进一步的读取数据库,验证输入的用户名称与密码是否和数据库表中的数据一致,具体的访问操作,在EntityBean中操作。
2. Hibernate的出现,给ejb的使用带来了新的机遇,你可以生成hibernate的相关对象,然后在SessionBean的方法中不调用EntityBean,而是直接调用Hibernate的对象,进行数据库的访问。这将带来更大的灵活性,并能提高程序开发的效率
3. 在测试程序中,可以使用Factory模式,简化SessionBean对象的创建,并能够提高重用。
4. xdoclet不止能够创建ejb的代码,还能够产生hibernate等很多代码,需要进行相关的配置即可,这需要对xdoclet的文档进行进一步的查看与研究,另外还能够自定义tag,例如生成javascript的校验代码。Xdoclet是个好东西,就是用起来很复杂。
5. eclipse的export功能可以输出ant的build.xml文件,不过这个配置文件很基本,你可以根据生成的build配置文件,进一步的扩充,使开发-〉测试-〉打包-〉部署自动化,能提高开发效率。