当提到开发J2EE服务端组件时,EJB是事实上的标准。会话bean被用作业务逻辑组件,并且主要是这两种类型:有状态的和无状态的。Bean的类型,有状态或无状态,是在布署时通过布署参数决定的。但是,有很多情况可能是在运行时才能决定是否需要有状态的bean。这篇文章中,我会解释一种用来在运行时选择是否有状态的bean的模式。
有状态和无状态的会话bean
会话bean为它的客户进行工作,通过在服务器端执行业务任务将业务逻辑的复杂度与客户屏蔽开来。无状态的会话bean不保持任何与客户的会话状态,发给bean的每一次请求需同时提供数据来进行请求处理。有状态的会话bean,与之相反,可以保持状态,并且该状态可以在与客户的多次会话中存在。
有状态的bean带来了支持会话状态的好处但同时付出了性能的代价。无状态bean具有更好的性能,但却又不具备与客户端的亲合力。EJB容器可以将无状态bean放入缓冲池中,并当有用户请求无状态bean时,从中选取任意一个bean来为其提供服务。
使用一个有状态还是无状态bean是设计时的选择,EJB的布署描述符描述了会话bean在EJB容器中的状态。并没有这样的先例,一个bean可以由处理结果来动态的改变其状态。
一个示例情景
考虑一个这样的情景:搜索组件需要设计为让客户端通过不同的参数来搜索数据仓库。一个有关顾客的搜索可能只是这个组件提供的各式各样的搜索中的一小部分。假如客户端以一个特定的顾客登录,那么这个组件的的后续搜索应该符合这个顾客的相关的配置。这些搜索应该包含符合这个顾客条件的产品的列表。假如没有登录,那么搜索应该返回系统默认的顾客的结果。出于保持示例通用的原因,我没有具体说明这个特定的搜索,但这个情景对于很多应用来说是很普通的情况。
我将阐述一种模式,它提供了动态地指定bean的有状态或无状态,并把此模式应用到上述示例业务情景中。使这种模式成为必要的动力在于:
·通常只有两个粗分类的会话EJB存在
·没有办法动态地将一个无状态地bean转化为有状态的bean
·业务情景要求根据处理结果将一个无状态的bean转换为有状态bean
·客户不应该承担创建一个有状态的bean责任,它应该在后台执行。
模式
我会细述解决以上问题的模式。创建一个focus类,它是一个POJO(普通的旧式java对象)。focus类拥有组件的逻辑实现。将组件同时模型化为有状态和无状态bean。会话bean将会实例化并使用focus来实现客户端请求。客户端,默认地,通过代理访问无状态bean的服务。假如处理结果表明客户需要一个有状态地用务,一个有状态的服务将会创建,有状态的服务将服务于客户端的后续请求。有状态的服务也使用focus类,并在调用focus类的方法时传递客户的状态。
类算法如图1显示,其中有模式中有关的主要的类以及展示了不同类之间的协作。
图1.无状态/有状态会话bean模式的算法
客户端
客户端是需要使用EJB组件提供的服务的任意Java对象。客户端使用代理来和服务交互。客户端并不知道有状态或无状态服务的存在,仅只是使用EJB提供的服务。
代理
这儿的代理是一种服务定位器的组合,也是一种业务代理模式。代理负责查找无状态的服务,并调用其上的服务。代理也负责处理返回的数据集,存储有状态的bean的处理器,假如有状态的处理器不存在则调用有状态服务。一个可选的步骤是代理解析作为参数传递的数据集,假如处理器指向了一个有效地有状态bean则调用有状态服务。
无状态服务
无状态服务是一个提供给客户组件服务的无状态EJB。无状态服务没有提供与客户端的会话状态,它是由代理来查找选取的。无状态bean使用focus类提供的功能。
有状态服务
有状态服务由无状态服务运行中创建的,当它决定客户的请求不应由无状态来提供。这由请求数据或者无状态服务中的处理逻辑来决定。有状态服务具有存储调用客户状态的能力并且将这些状态应用到focus类中。
数据集
数据集是由服务器返回给客户端的一个通用的返回。数据集具有作为由方法调用产生的服务返回的实际数据,并且还具有与返回客户相关的配置信息。数据集同时具有有状态服务的处理器,它被代理用来请求有状态服务所需要的数据。
Focus类
Focus类有对应组件中的主业务逻辑。Focus类包含了基本功能并不包含任何状态。它基于传入的数据进行处理,并在方法调用之间不保持状态。作为一个经验,好的实践是在focus类中实现这些方法,而focus对于有状态和无状态服务来说是平等的。
协作
客户端实例化代理并通过它调有组件上的任意方法。代理的逻辑是查找无状态bean,并调用相应无状态服务上的对应方法。无状态bean处理请求并调用focus类得到结果。处理的结果以数据集对象的型式返回给客户端。
假如在处理请求的过程中,无状态服务决定服务需要为将来的处理存储请求的状态,那么无状态服务则创建一个有状态服务。有状态bean的处理器存储在数据集对象中返回给客户。当然,返回的数据集也包含了客户端所请求的返回数据。代理将会在返回给客户之前解析数据集对象,并将有状态bean的处理器作为状态存储起来。当客户对代理进行下一次调用时,代理将会调用作为代理的状态一部分的有状态bean的处理器。
有状态bean可以使用代表了客户的状态,并将它作为附加地属性传递给focus类。Focus类不需要关心是有状态服务的请求还是无状态服务的请求。从有状态bean而来的请求拥有附加的状态信息,因此可以主结果反映出代表客户的任意的会话状态。图2以UML流程图展示了它们之间的关系。
图2.模式参与者之间的交互序列
应用此模式
我会用上述模式来解决示例情景的问题。客户端使用代理来使用查找组件的功能。代理将会查找代码并暴露出服务所暴露的相同的一组方法。客户端调用代理上的searchParam1方法,而代理调用无状态服务上的searchParam1方法。
假如客户端调用了login方法,代理就调用无状态服务上的相对应的方法。假如查找的结果是单个的顾客,无状态服务就创建一个有状态服务并将客户细节传给有状态服务的ejbCreate方法。无状态服务也创建一个DataSet的实例,并设置用户数据,同时也在其上设置有状态bean的处理器,在DataSet上代理保存了处理器并将DataSet传递给客户。
客户现在需要得到与自己相关的一组产品的列表。客户端调用代理的retrievePRodUCts方法。代理得到有状态的处理器,并调用有状态bean的相关的方法。
另外的情景
模式是通用的并能用在不同的应用上。上述模式可以用在需要使用一个值列表处理器时。使用无状态bean可以进行一般的搜索,并且当结果的大小超过能接受的最大值时,我们可以创建一个有状态bean并将结果传递到以一个数据集对象的形式存在的缓存中。客户端通过代理请求下一个记录集,代理可以转到有状态bean并存储细节,比如,最后的记录处理,最大的对象记录等等。
相关模式
服务定位器模式:服务定位器模式降低了客户的复杂度,这种复杂度由客户端的依靠,操作需要,查找和创建过程产生,这些都是与资源紧密联系的。为了消除这些问题,这个模式提供了一种技术来抽象所有的依靠性和网络细节到服务定位器中。
业务代理模式:业务代理担当客户端的业务抽象;它提供了抽象同时隐藏了业务服务的实现。使用业务代理降低了表现层和系统服务层之间的耦合。依靠实现策略,业务代理可以保护客户端免受系统服务API可能的错误的侵袭。潜在地,这也就降低了当业务服务API或底层实现变化时影响到表现层代码。
值列表处理器: 值列表处理器模式创建一个值列表处理器来控制查询功能和返回结果的缓存。值列表处理器直接访问一个DAO(数据访问对象)来执行所需的查询。值列表处理器将DAO的返回结果放到一个传输对象的集合中。客户端向值列表处理器发出请求。值列表处理器返回所需的查询结果。值列表处理器实现了迭代器模式(GoF)来解决上述情况。
结论
动态的EJB状态模式帮助模型组件同时具有了有状态和无状的行为。一个使用些模式的组件提供了无状态会话bean的性能和满足了客户端的有状态的需要。使用此模式的客户端拥有一个简单的统一的接口,并且不感知内部有状态bean和无状态bean之间的切换。这个模式和其它J2EE模式是自然地匹配的,比如业务代理模式和服务定位器模式。