有状态会话 Bean 是为了保持关系
- 在对 Bean 的不同调用间有用的状态被保留:无状态会话 Bean 不保留状态 (即其名称的含义)
- 通过一系列调用完成业务
- 有状态会话 Bean 示例:
- 购物车
- 出纳
- Wizard, SmartGuide, ...
一个示例
- 使用 "SeatFinder" bean 在一场歌剧中找出最佳座位
- 设置您的喜好,然后让 Bean 开始查找
...
SeatFinder finder = getSeatFinder().create();
finder.setShow("U2 - Live from Kmart");
finder.setLikeFloorSeats(false);
finder.setLikeGeneralAdmission(false);
finder.tryCity("Toronto");
finder.tryCity("Montreal");
finder.tryCity("Cornwall");
finder.tryDate("2001-02-04");
finder.tryDate("2001-02-05");
Seat seat = finder.findBestSeat();
... |
|
有状态会话 Bean 不是持久性的
- 会话 Bean 不会在服务器宕机后继续存活
- 会话设定有操作超时,一旦超时 Bean 将被回收
- 有状态会话 Bean 只保存短暂的数据
- 数据用来处理业务,然后被抛弃
- 钝化/激活只是容器管理其状态的方法
- 会话 Bean 可以访问数据库:如其它类一样,使用 JDBC
- 没有自动的或者生命周期的管理
不要使用构造函数
- 在 "create" 方法中设置 Bean 的状态
- 记住 Bean 的实例是放在池中的:通常不是创建新的实例,而是通过实例池来共享已经存在的实例
| 错误 |
public Order() {
setItems(new Vector());
} |
|
public class Order extends SessionBean {
private Vector items = new Vector();
...
} |
|
| 正确 |
public void ejbCreate() {
setItems(new Vector());
} |
|
每个用户一个 Bean
- 真正的 Bean 实例是共享的
概念上而言,每个用户有自己一个实例 - 当资源紧张时,Bean 可能被 "交换出去"
在 WebSphere 中,Bean 的状态是可被复制导数据库中
Bean 实例是共享的
EJBObject 通过使用钝化 (passivation) 和激活 (activation) 来共享实例
钝化 (Passivation)
- 当内存资源紧张时,通过将 Bean 实例交换出去可以回收其所占内存
- 最近未使用的 Bean 被选中来 "passivation"
- 在 ejbPassivate 方法中应该关闭使用中的任何资源 (如数据库连接、SOCKET 连接等)

激活 (Activation)
当 Bean 的状态被载入内存后,将出现 Activation
在 ejbActivate 方法中重新建立与资源的连接

Activation 和 Passivation 代码示例
打开一个文件继续添加
public void ejbActivate() {
setOutput(new FileOutputStream(new File(getFileName()), true));
} |
|
在被交换出去前关闭文件 |
public void ejbPassivate() {
getOutput().close();
} |
|
有状态会话 Bean 生命周期
注意: - Bean 在下列情况下被回收:
- 超时 (无论是激活的或钝化的)
- 被调用 remove()
- Passivation 是根据 Least-Recently-Used 算法
- Activation 由对 Bean 的方法调用产生
- afterBegin()
beforeCompletion() afterCompletion() 方法仅对实现 SessionSynchronization 接口的 Bean 有效 - 在一个事务环境下,调用的方法必需具有相同的事务类型
|  |
序列化 (Serialization)
- WebSphere 使用 Java 的 serialization 来交换出您的Bean 的状态
- Java 序列化机制可以将整个对象的状态输出到一个流上
- 对象可以再从其原始状态重新构建回来

Serialization 示例代码
...
FileOutputStream fileStream = new FileOutputStream("c:\\product.obj");
ObjectOutputStream out = new ObjectOutputStream(fileStream);
out.writeObject(product);
out.close()
... |
|
...
FileInputStream fileStream = new FileInputStream("c:\\product.obj");
ObjectInputStream in = new ObjectInputStream(fileStream);
Product product = (Product)in.readObject();
out.close()
... |
|
真正发生了什么
- Bean 的实例没有被序列化
如果 Bean 实例本身被序列化,当它被重新构建时一个新的实例需要被创建这就丢失了实例池的意义 - Bean 的状态被序列化
每个属性需要被单独考虑
什么会被序列化?
- 一个可序列化 (serializable) 的对象
- 一个 null
- 对特定不可序列化对象的直接引用:
- 一个 Bean 的 home 或 remote 接口的引用
- 对 SessionContext 的引用
- 对不可序列化部分的直接引用的序列化
- 容器必需对不可序列化的 EJB 在钝化时保存它们的 remote 和 home 接口
- 也许依赖于 Java 的 java.io.ObjectOutputStream 和 java.io.ObjectInputStream 中的对象替换技术来实现
部分序列化
- 构成状态的每样属性必需是可序列化的
- 所有属性的内容必需包含可序列化的值
- Helper 类
- 否则容器可以选择在钝化时回收该 Bean 实例
部属有状态会话 Bean
Bean 也有属性:从 Server Configuration 中可以访问到它们

常见错误
- 忘记将 Bean 设为 "stateful"
- 注意要好好测试您的 Bean
- "Ghost" 数据 (来自其它实例) 可能意味着您忘记将 "state management" 属性设正确
- 在初始函数和构造函数中设置状态
也会产生 "Ghost" 数据
本章讲述内容
- 会话 Bean 的实例是被共享的
- 在 create 方法中初始化 Bean,而不是在构造函数或初始化函数中