转载自:http://www.java-cn.com/technology/technology_detail.jsp?id=781
开发Beans
一、会话Beans介绍
按功能可把EJB分为两类:Session Beans 与 Entity Beans。
企业级Bean类
为了使bean可以在任一容器中工作,bean必须被附在接口中。在EJB中,在enterprise bean class中提供了企业级bean组件的实现。这是个简单的遵循接口的java类。
一个enterprise bean class 包含对组件的实现细节。会话bean的实现不同于实体bean的实现,
一个Session Beans针对单一的客户完成一次连接或会话,其生存直到客户完成连接与会话,或系统意外中止。当一个新的客户从EJB Server访到一个Session Beans时,那么EJB Container创建一个新的Session Beans实例,其运行直到会话结束,Session Beans必须实现接口javax.ejb.SessionBean。
Entity Beans实现接口javax.ejb.EntityBean,其描述了特定数据源中的数据,能长时间存在于EJB Container中,不会随系统的意外中止而消失,并且可以让多个客户同时访问。
EJB规范定义了许多bean类能够实现的标准接口。定义了所有的bean类必须有的方法。容器调用这些方法用来管理bean。
所有bean类(无论是会话bean还是实体bean)必须实现的最基本的接口是javax.ejb.EnterpriseBean接口。
public interface javax.ejb.EnterpriseBean extends java.io.Serializable
{
}
值得注意的是:它继承了java.io.Serializable。
所有的会话bean必须实现javax.ejb.SessionBean,
所有的实体bean必须实现javax.ejb.EntityBean.
EJB对象
当客户想使用enterprise bean class的一个实例时,客户不必直接在实际的实例上调用方法,调用过程被EJB容器截取,bean实例被容器中对象所代表。
1、Enterprise bean class不能通过网络直接被调用,我们知道EJB容器可以操纵网络,因此它通过容器将bean包装成可在网络上使用的对象。
2、通过截取请求,容器可以自动执行许多必要的管理工作。
3、EJB容器可以跟踪哪个方法被调用,在系统管理者的用户接口上显示其用法等等。
因此,EJB容器可以看作间接的存在于客户代码和bean之间的层。这个间接的层使用单独的网络对象来表示自己,这个对象称为EJB对象。
EJB对象作为容器物理的部分;所有的EJB对象都有针对容器特殊要求的代码。因此,容器提供商提供专门工具,用来自动为EJB对象产生类文件。
远程接口
我们前面了解到,bean客户调用EJB对象上的方法来代替调用bean,为了执行它,EJB对象必须复制bean类中的每个业务方法。但是,怎样才能使自动产生的EJB对象知道复制了哪个方法呢?这就用到了bean提供者写的一个特殊的接口,这个接口复制所有的与bean类相关联的业务逻辑方法。这个接口被称为远程接口。
这个接口必须遵循EJB规范的定义,所有的远程接口必须从sun公司提供的通用接口继承而来,即javax.ejb.EJBObject。
EJB对象
public interface javax.ejb.EJBObject
extends java.rmi.Remote
{
public abstract javax.ejb.EJBHome getEJBHome()
throws java.rmi.RemoteException;
public abstract java.lang.Object getPrimaryKey()
throws java.rmi.RemoteException;
public abstract void remove()
throws java.rmi.RemoteException,
javax.ejb.RemoveException;
public abstract javax.ejb.Handle getHandle()
throws java.rmi.RemoteException;
public abstract boolean isIdentical(javax.ejb.EJBObject)
throws java.rmi.RemoteException;
}
以上是对于所有EJB对象必须拥有的方法,你不需实现这些方法,这些方法的实现,生成EJB对象时由容器自动生成。
客户端代码通过调用javax.ejb.EJBObject的方法来和bean协同工作。
Java RMI和EJB对象
你应当注意到:java.ejb.EJBObject继承了Java.rmi.Remote。Java.rmi.Remote接口是java远程方法调用(RMI)的一部分,任一个实现java.rmi.Remote的对象都是rmote对象,它可以被另外的java虚拟机所调用。
被容器提供的EJB对象实现了远程接口,同时也间接实现了java.rmi.Remote,这样也就意味着你的EJB对象是完全符合网络需要的,可以被网络上的其他java虚拟机调用。当然,EJB接口也必须遵守EJB规范。
EJB远程接口必须遵守java的RMI远程接口规范。例如:错误处理,二者相同。
远程接口同样也必须遵守java RMI参数传递规范。不是什么都可以通过VM方法调用来在网络上传递,传递的参数必须符合RMI类型。
EJB也继承了RMI的优点,对于RMI,你正在调用的远程对象的物理地址是不可见的。这个特点同样也适用于EJB。客户代码不必关心正使用的EJB对象是在邻近的计算机上还是从internat传递来的。这样,EJB对象可以和客户端处在同一个java VM中。
EJB保证了本地分布式组件的透明度。这种透明对于多层配置来说是非常必要的。客户端代码是非常容易移植的,不受限于特殊的多层配置。EJB容器可以以最佳化方式在本地执行。
Home对象
我们看到,客户端代码处理EJB对象,而从不直接操作beans。那么,客户端如何得到EJB对象的参考呢?
客户端不直接将EJB对象实例化。因为EJB对象可以存在于不同的机器中。同样的,EJB使本地透明化,因此客户端不知道它的确切所在。
客户端代码通过EJB对象工厂得到EJB对象的参考。EJB规范里称这种工厂为home对象。它主要起一下作用:
建立EJB对象。
找到已经存在的EJB对象。
删除EJB对象。
在一些细节方面,EJB对象工厂同EJB对象的特征相同。
Home接口
Home接口简单的定义了建立、删除和寻找EJB对象的方法。容器的home对象实现了home接口。
通常,EJB定义了所有home接口必须支持的许多方法,这些必须的方法被定义在javax.ejb.EJBHome接口上,home接口必须继承Java.ejb.EJBHome接口。
public interface javax.ejb.EJBHome
extends java.rmi.Remote
{
public abstract EJBMetaData getEJBMetaData()
throws java.rmi.RemoteException;
public abstract void remove(Handle handle)
throws java.rmi.RemoteException
javax.ejb.RemoveException;
public abstract void remove(Object primaryKey)
throws java.rmi.RemoteException,
javax.ejb.RemoveException;
}
javax.ejb.EJBHome接口
注意javax.ejb.EJBHome继承了java.rmi.Remote,这意味着home接口同样也支持RMI远程对象,传递的参数和RMI也相同。
Home对象
所有home对象所需的方法
配置描述符
配置描述符允许EJB容器向企业级的bean组件提供隐含的中间件服务。隐含的中间件服务是bean可以获得不必将任何中间件API解码,可以自动获得服务的一种服务。
Bean的特殊属性
最后,你还需要有一个基于java的bean的属性文件。Bean在运行时读这些属性,这些属性在使用bean函数时会被用到。
Ejb-jar文件
一旦生成bean的类、home接口、远程接口、配置描述符和bean的属性,我们就可以把它们打包成一个实体。这个实体称作Ejb-jar文件。这是个压缩文件。
建立Ejb-jar文件
什么是会话bean
一个Session Beans针对单一的客户完成一次连接或会话,其生存直到客户完成连接与会话,或系统意外中止。Session Beans必须实现接口javax.ejb.SessionBean。
会话bean的生存期
会话bean和实体bean的主要不同是它们的生存期的长短。会话bean的生存期短。与客户的会话时间相当。在与客户连接端开时,EJB容器会破坏会话bean。
相反,实体bean可以存活相当长的时间,实体bean是永久存取的一部分,例如:数据库。
会话bean不能保存永久的存储数据,但是,它可以进行数据库操作。
所有的会话bean都需要管理callback方法,容器定时的调用它,用来对bean的重要事件发出警告。这个方法仅能被容器调用。
Conversational versus Nonconversational Session Beans
如何写会话Bean
写会话bean的类,必须实现javax.ejb.SessionBean接口
public interface javax.ejb.SessionBean
extends javax.ejb.EnterpriseBean
{
public abstract void setSessionContext(SessionContext ctx)
throws java.rmi.RemoteException;
public abstract void ejbPassivate()
throws java.rmi.RemoteException;
public abstract void ejbActivate()
throws java.rmi.RemoteException;
public abstract void ejbRemove()
throws java.rmi.RemoteException;
}
会话bean和实体bean都继承了javax.ejb.EnterpriseBean接口
让我们详细看看接口中的各种方法:
setSessionContext(SessionContext ctx)
容器调用这个方法来通过会话上下文与bean连接。Bean可以通过会话上下文向容器查询当前事物的状态和当前的安全状态等。
import javax.ejb.*;
public class MyBean implements SessionBean {
private SessionContext ctx;
public void setSessionContext(SessionContext ctx) {
this.ctx = ctx;
}
...
}
ejbCreate(…)
用来初始化你的会话bean,可以定义多个不同参数的ejbCreate()方法来用不同的方法初始化bean。
import javax.ejb.*;
public class MyBean implements SessionBean {
private int memberVariable;
public void ejbCreate(int initialValue) {
this.memberVariable = initialValue;
}
...
}
ejbCreate()方法是容器可以调用的callback方法,客户端代码不能调用它,因为客户端不能直接处理beans??他们必须通过容器,但是客户端必须采用某种方法向ejbCreate方法传递参数,客户端提供初始化参数。Home接口是客户端用来初始化调用的接口工厂。你必须在home接口中复制每一个ejbCreate()方法,例如:如果在bean类中你有下面的ejbCreate方法
public void ejbCreate(int i) throws ...
那么你必须在你的home接口里有下面的create()方法
public void create(int i) throws ...
客户端调用home接口中的create()方法,将参数传递给ejbCreate()。
EjbPassivate()
如果出现太多的实例bean,EJB容器可以将它们中的一些钝化,将它们写到临时的存出空间例如数据库或文件系统。容器释放它们所申请的空间。
import javax.ejb.*;
public class MyBean implements SessionBean {
public void ejbPassivate() {
<close socket connections, etc...>
}
...
}
ejbActivate()
当客户需要使用被钝化的bean时,容器将被钝化的bean重新导入内存,激活它们。
Bean又被导致内存,这时需要重新得到bean所需要的资源。
import javax.ejb.*;
public class MyBean implements SessionBean {
public void ejbActivate() {
<open socket connections, etc...>
}
...
}
ejbRemove()
当容器将会话bean实例remove掉时,调用此方法。所有的bean都有这种方法,它没有参数,它将释放所有以分配的资源。
import javax.ejb.*;
public class MyBean implements SessionBean {
public void ejbRemove() {
<prepare for destruction>
}
...
}
容器可以在任何时候调用ejbRemove()方法,但如果遇到异常,则有可能禁止容器调用此方法。
业务方法
应该定义一些解决业务问题的方法:例如:
import javax.ejb.*;
public class MyBean implements SessionBean {
public int add(int i, int j) {
return (i + j);
}
...
}
因为客户端要调用这些方法,因此,你必须在bean的远程接口中列出这些方法。
如何调用会话beans
我们现在来看客户方,客户方要通过使用一个或多个bean解决一些现实的问题。
有两种不同的客户方:
Java RMI-based clients:这种客户方通过使用JNDI定位对象,使用JTA控制事务。
CORBA客户方:客户方也可以使用CORBA标准来写。CORBA客户方使用CORBA名字服务(COS Naming)在网络上定位对象,使用CORBA的OTS控制事务。
无论是用CORBA还是RMI,典型的客户端代码都必须实现:
1、 定位Home接口
2、 使用Home接口建立EJB对象
3、在EJB对象上调用业务方法
4、清除EJB对象
定位Home接口
客户端使用JNDL定位Home对象。
J2EE中JNDL的作用
J2EE的目标之一是使应用程序实现“write once,run anywhere”。任何的运行在企业级配置的java代码在多层结构中应该是不受约束的。因此必须实现定位的透明化。
J2EE通过使用JNDL来实现定位的透明化。已有目录服务的产品如Netscape的Directory Server,微软的Active Directory,IBM的Lotus Notes。
通常我们使用目录服务存储用户名、密码、机器位置、打印机位置等等。J2EE扩展目录服务存储资源的本地信息,这些资源也可以是Home对象、企业级bean的环境属性、数据库驱动、信息服务驱动和其他资源。使用目录服务,在些应用程序代码时可以不必关心确切的机器名字和机器地址,这样保证了代码的可移植性。无论资源在何处,都不需要重新编译代码。
JNDL通过为本地用户、机器、网络、对象和服务提供一个标准接口向企业级配置中增加值。
为了在J2EE中定位资源,必须实现以下两步:
1、 用配置描述符中的“绰号”关联资源,J2EE将向资源绑定绰号。
2、 资源的客户端使用JNDL中的绰号定位资源。
怎样使用JNDL定位Home对象
客户端不必关心Home对象在网络的什么地方。JNDL为Home对象提供绰号来定位Home对象。通过绰号可以得到Home对象的参考。
具体点,客户端代码必须执行以下几步来通过JNDL得到参考。
1、 建立环境。必须配置将要使用目录服务,包括为验证身份所需的用户名、密码。
2、 建立初始的上下文。初始上下文是连接目录结构的本地出发点。通过初始上下文得到设定的环境属性。
3、 得到Home对象。执行lookup()操作,返回一个RMI远程对象。
得到Home对象的参考
/*
* Get System properties for JNDI initialization
*/
Properties props = System.getProperties();
/*
* Form an initial context
*/
Context ctx = new InitialContext(props);
/*
* Get a reference to the home object - the
* factory for EJB objects
*/
MyHome home = (MyHome) ctx.lookup("MyHome");
建立EJB对象
得到Home对象以后,可以将Home对象作为建立EJB对象的工厂。调用create()方法建立EJB对象。
MyRemoteInterface ejbObject = home.create();
无参数是因为无状态beans不需要初始参数。
调用方法
客户端有了EJB对象以后就可以通过它来调用方法。当客户端在EJB对象上调用方法时,EJB对象必须选择一个bean实例来应答。EJB对象可以建立一个新的实例或是重用已经存在的实例。
ejbObject.add();
破坏EJB对象
调用EJB或Home对象上的remove()方法来破坏EJB对象。
ejbObject.remove();