第 2 章 Enterprise JavaBeans
2.3 EJB 体系结构(二)
第 2 章 Enterprise JavaBeans
中 我们声明了getAuthor()方法 客户端的应用程序就可以调用这个方法
2.3 EJB 体系结构(二)
2.3.1 EJB Object
EJB Object(EJB 对象)是网络上的可视对象(对于 Remote Interface 而言是可视的) 它包 含 了 stub 和 skeleton 作 为 EJB 类 的 代 理 EJB 组 件 的 Remote Interface 继 承 了 javax.ejb.EJBObject 接口 而 EJB Object 则实现了这个 Remote Interface 使得 EJB 类与 Remote Interface 之间搭起了一座桥梁 对于每个 EJB 都有一个定制的 EJB Object EJB Object 隔离了 EJB 类 使得 EJB 类在网络中是不可见的 不过 EJB Object 提供了 stub 和 skeleton Remote Interface 通过这两条路径 与 EJB 类相互通信 所谓的 EJB 类就是真正 实现了商业逻辑 商业方法的类 它有两种类型 一种是Session EJB(会话 EJB) 一种是 Entity EJB(实体 EJB) 在前面 我们对 EJB Object 与 EJB 类不加区分 统一称为 EJB Object 或者EJB 对象 但是由此开始 我们就需要把它们严格区分开来 打一个不十分贴切的比 方 EJB 类相当于一个人 那么 EJB Object 就相当于人身上的衣服
实现Remote 接口的 EJB Object 其实是一个 RMI 服务器对象 注意 EJB 类本身不是一 个Remote Object 它在网络上是不可视的 当 EJB 容器实例化了这个 EJB Object 后 EJB 容器会接着初始化EJB 类的实例引用 使得 EJB Object 能够处理 Remote Interface 传送过 来的请求 代理EJB 类所实现的商业方法的调用
EJB Object 是由提供 EJB 容器 EJB Server 的厂商实现的 厂商的实现维护了 EJB Object 实例和 EJB 实例的一对一关系 因为 Remote Interface 包含了 EJBObject 接口的方法 所以EJB 类不必显式地实现这个接口 虽然它提供了 Remote Interface 所列出的商业方法的 实现 因为EJB Object 必须正式地实现 EJB 的 Remote Interface EJB 容器在部署 EJB 时会 自动产生EJB Object 的源代码 并编译为二进制文件 这些产生的 EJB Object 源代码实现 了Remote Interface 典型的 EJB Object 有一个独特的类名 作为 EJB Object 和 EJB 类的联 系
2.3.2 Session EJB
EnterpriseBean 是 EJB 开发者编写的提供应用程序功能的类 简称为 EJB 类 在上文 中我们已经有所提及 EnterpriseBean 是 EJB 组件模型中具体实现商业逻辑 商业方法的部 分 EnterpriseBean 有两种 会话 EJB 与实体 EJB EJB 开发者可以选择创建会话 EJB 或实 体EJB 通过实现不同的接口声明 不同的部署描述符来加以区分
对于会话 EJB 必须实现 javax.ejb.SessionBean 接口 对于实体 EJB 必须实现 javax.ejb.EntityBean 客户端应用程序不会也不能够直接访问 EnterpriseBean 中的任何方法 客户端应用程序通过Remote Interface/EJB Object 间接调用 EnterpriseBean 中的方法 EJB Object 就像一个代理服务器一样 在把调用通过 EJB Object 传递时 容器开发商通过包装 EJB Object 的编码插入自己的功能 这称为方法插入 方法插入的一个例子是为每个方法 调用创建一个新的事务上下文 当方法返回到 EJB Object 时提交(commit)或回滚(rollback) 事务
第一部分 JSP 技术与 J2EE 技术
当使用EJB 容器厂商的发布工具在分发部署 EJB 时 发布工具会产生 EJB 组件的 EJB Object 的一个 stub 和 skeleton 实际上发布工具并不创建 EnterpriseBean 本身的 stub 和 skeleton 也并不需要创建 因为 EnterpriseBean 不会通过网络直接被访问到 EJB Object 才是真正的网络对象
EJB 容器也可以调用 EnterpriseBean 中的某个方法 例如 EJB 容器保证当一个 EnterpriseBean 实例生成后 Home Object 中的 create()的任何参数会传递到 EnterpriseBean 相应的 ejbCreate()方法 EnterpriseBean 还有其它的接口和要求 然而 会话 EJB 和实体 EJB 的要求是不同的 下面我们就来介绍会话 EJB(Session EJB) 下一小节再介绍实体 EJB(Entity EJB)
会话EJB 是一种通过 Home Interface 创建并对客户端连接专有的 EnterpriseBean 会话 EJB 的实例一般不与其它客户端共享 这允许会话 EJB 维护客户端的状态 会话 EJB 的一 个例子是购物车 众多顾客可以同时购物 往他们自己的购物车中添加商品 而不是向一 个公共的购货车中加私人的货物
我们可以通过定义一个实现javax.ejb.SessionBean 接口的类来创建一个会话 EJB 该接 口(SessionBean 接口)的定义如下
public void setSessionContext(SessionContext ctx) throws EJBException java.rmi.RemoteException;
public void ejbRemove() throws EJBException java.rmi.RemoteException;
public void ejbActivate() throws EJBException java.rmi.RemoteException;
public void ejbPassivate() throws EJBException java.rmi.RemoteException;
javax.ejb.EnterpriseBean 是一个空接口 是会话 EJB 和实体 EJB 的超类 下面是一个 简单的会话EJB 的例子
程序清单 2.3 //File Name:TestBean.java //Author:fancy
//Date:2001.5.1
//Note:to create a session EJB import java.rmi.*;
import javax.ejb.*;
public class TestBean implements SessionBean {
private SessionContext sessionContext;
public void ejbCreate() {
}
public void ejbRemove() {
}
public void ejbActivate() {
第 2 章 Enterprise JavaBeans
}
public void ejbPassivate() {
}
public void setSessionContext(SessionContext context) {
sessionContext = context;
}
public String getAuthor() {
return "Rainbow";
} }
在上面的例子中 geAuthor()方法是 EJB 开发者自己定义的商业逻辑 会话EJB 的交换(切换)
EJB 容器开发商可以实现把会话 EJB 的实例从主存移到二级存储中的交换机制 这可 以增加在特定的一段时间内实例化的会话EJB 的总数 EJB 容器维护一个会话 EJB 实例的 时间期限 当某个会话EJB 的不活动状态时间达到这个阙值 EJB 容器就把这个会话 EJB 实例拷贝到二级存储中并从主存中删除 EJB 容器可以使用任何机制来实现会话 EJB 的持 久性存储 最常用的方式是通过EJB 的串行化 这个交换机制与 Windows 操作系统的内存 交换机制有一定的相似之处
活化和钝化
为了支持EJB 容器厂商提供会话 EJB 的交换 EJB 规范定义了钝化 把会话EJB 从 主 存 转 移 到 二 级 存 储 的 过 程 活 化 把 会 话 EJB 恢 复 到 主 存 中 去 的 过 程 在 javax.ejb.SessionBean 接口中声明的 ejbActivate()和 ejbPassivate()方法 允许 EJB 容器通知 已经被活化的会话EJB 的实例 它将要被 EJB 容器钝化 EJB 开发者可以用这些方法释放 或者恢复处于钝化状态的会话EJB 所占有的值 引用和系统资源 一个可能的例子是数据 库连接 作为有限的系统资源 不能被处于钝化状态的会话EJB 所使用
有了这些方法就使得不必再使用 transient 事实上 使用 transient 可能是不安全的 因为串行化机制自动地把transient 的值设为 null 或 0 而通过 ejbActivate()和 ejbPassivate() 方法显式地设置这些参数更好一些 依靠Java 的串性化机制把 transient 的值设成 null 也是 不可移植的 因为会话 EJB 有可能被部署在不使用 Java 的串性化机制获得持久性的 EJB 容器中 这时该行为会发生改变 如果EJB 容器不提供实现 EJB 交换的机制 那么这些方 法将永远不会被调用
当客户端程序调用 EJB 的商业方法时 钝化的会话 EJB 被自动激活 当 EJB Object 收到方法调用的请求时 它唤醒EJB 容器所需要活化的会话 EJB 当活化完成时 EJB Object 代理对会话 EJB 的方法调用 如果会话 EJB 参与一个事务 那么它不能被钝化 把会话 EJB 放在主存中更有效率 因为事务通常在很短的时间内完成 如果会话 EJB 没有钝化前
第一部分 JSP 技术与 J2EE 技术
必须释放或活化前必须重置的状态 那么这些方法(指的是 ejbActivate()和 ejbPassivate()方 法)可置空 在大多数情况下 EJB 开发者不必在这些方法中做任何事
会话EJB 的状态管理
会话 EJB 的部署描述符必须声明该 EJB 是有状态或者是无状态的 一个无状态 (Stateless)的会话 EJB 是在方法调用间不维护任何状态信息的 EJB 通常 会话 EJB 的优点 是代替客户端维护状态 然而 让会话EJB 无状态也有一个好处 无状态 EJB 不能被钝化 因为它不维护状态 所以没有需要保存的信息 EJB 容器可以删除会话 EJB 的实例 客户 端应用程序永远不会知道无状态EJB 的删除过程 客户端的引用是通过 EJB Object 如果 客户端应用程序稍后又调用了一个商业方法 则EJB Object 通知 EJB 容器再实例化一个新 的会话EJB 实例 因为没有状态 所以也没有信息需要恢复
无状态EJB 可以在不同的客户端应用程序间共享 只是在某一时刻只能有一个客户端 执行一个方法(独占状态) 因为在方法调用间没有需要维护的状态 所以客户端应用程序 可使用任何无状态会话EJB 的实例 这使得 EJB 容器可以维护一个较小的可用 EJB 的缓冲 池 以节省主存 因为无状态 EJB 在方法调用间不能维护状态 因此从技术上讲在 Home Interface 的 create()方法不应有参数 在创建 EJB 时向 EJB 传递参数意味着在 ejbCreate()返 回时需要维护EJB 的状态 而且 EJB 实例一旦删除 经由 EJB Object 调用商业方法的结 果使得EJB 容器必须重新创建一个无状态的 EJB 这时在最开始创建 EJB 时所用的参数就 不存在了 EJB 容器开发厂商的部署分发工具应该能检查 Home Interface 如果是无状态对 话的EJB 应该保证其不包含带参数的 create()方法
2.3.3 Entity EJB 实体EJB 的角色
实体EJB 用来代表底层的对象 最常见的是用实体 EJB 代表关系型数据库中的数据 一个简单的实体EJB 可以定义成代表数据库表的一个记录 也就是每一个实例代表一个特 殊的记录 更复杂的实体EJB 可以代表数据库表之间的关联视图 在实体 EJB 中还可以考 虑包含厂商的增强功能 如对象-关系映射的集成
通常用实体类代表一个数据库表比代表多个相关联的表更简单且更有效 反过来可以 轻易地向实体类的定义中增加关联 这样可以最大地复用cache 并减小旧数据的表现
实体EJB 和会话 EJB 的比较
看起来会话EJB 好象没什么用处 尤其对于数据驱动的应用程序 当然事实并不是这 样 因为实体EJB 譬如说 代表底层数据库的一行纪录 则实体 EJB 实例和数据库记录 间就是一对一的关系 因为多个客户端程序必须访问底层记录 这意味着 不同于会话EJB 客户端程序必须共享实体EJB(事实上 这个功能也可以使用会话 EJB 来完成 不过效率不 高 消耗系统资源过多 有多少个客户端程序在运行 就需要多少个会话EJB 在服务端运 行 而使用实体EJB 就好多了 只需要一个实体 EJB 在运行即可) 因为实体 EJB 是共享 的 所以实体EJB 不允许保存每个客户端的信息 会话 EJB 允许保存客户端的状态信息 客户端应用程序和会话EJB 实例间是一对一的 实体 EJB 允许保存记录的信息 实体 EJB
第 2 章 Enterprise JavaBeans
的实例和记录间是一对一的 一个理想的情况是客户端通过会话EJB 连接服务器 然后会
的实例和记录间是一对一的 一个理想的情况是客户端通过会话EJB 连接服务器 然后会