EJB会话bean(二):1.并发和线程安全,构造服务器端应用程序的重点在于可以是客户端同时共享,会话bean用于处理客户端请求,他们支持高并发。容器利用很多技术“自动”确保你不必担心并发和线程安全方面的问题。
2.远程处理Web服务
会话bean支持基于Java远程方法调用的本地访问和基于简单对象访问协议(Simple Object Access Protocol,SOAP)的Web服务器远程访问。
3.事务和安全管理
会话bean与纯基于配置的事务、授权和验证完全可以支持这些需求。
4.计时器服务和拦截器
拦截器(interceptor)是AOP的EJB版本。AOP可以将“横切”事项隔离到他们自己的模块中,通过配置跨程序应用。横切事项包括审核和登录等在应用程序内重复出现但与业务逻辑无直接关联的事项。计时器服务时轻量型应用程序调度器的EJB版本。使用计时器服务可以将会话bean转换为循环或非循环的调度任务。
会话bean基础
每个会话bean实现都具有两个独特部分——一个或多个接口和一个bean实现类。所有会话bean都必须分为两个部分, 因为客户端不能直接访问bean实现类,它们必须通过业务接口使用会话bean。一个EJB可以具有多个接口,bean支持的每个接口都必须显式地包含在bean实现类的implements字句之中。会话bean实现类永远都不能是抽象的, 也就是说业务接口委托的所有方法都必须在类中实现。EJB实现类可以拥有通过任何接口都不能访问的非私有方法,这样的方法可以用于创建智能单元测试框架和实现生命周期回调。此外,EJB bean类可以利用 面向对象的继承, 可以利用这种策略支持应用程序的定制框架。例如,你可以把常用的逻辑放在 POJO父类中, 组bean可以继承它。bean类中必须具有无自变量构造器。因为当客户端调用bean时, 容器会调用此构造器创建bean实例。注意, 如果Java类中没有构造器, 编译器会插入默认的无自变量构造器。会话bean类可以是另一个会话bean或任何其他POJO的子类。可以在bean类或者超类中定义业务方法和生命周期回调方法。但是,EJB 3会话bean支持的注解继承有若干限制。例如, 当你部署BidManagerBean时,PlaceBidBean 超类中指定的bean类型注解@Stateless或@Stateful会被忽略。 但是,超类中用于定义生命周期回调方法和资源注入的所布注解都会被bean类继承。所有业务方法都定义为公有的,不能是最终的或静态的。如果你暴露EJB远程业务接口中的方法,那么要确保方法的自变量和返回类型会实现java.io.Serializable接口。
bean生命周期回调
会话bean有生命周期,也就是说bean会经历预定义的一组状态转移。容器几乎管理会话bean的每个方面,这就是说客户端和bean都不负责决定何时创建bean实例、何时注入依赖、 何时销毁bean实例或何时采取优化措施。管理这些动作支持容器提供构成使用EJB的一些有真正价值的抽象,包括XML依赖注入、自动化事务管理、AOP、透明安全管理,等等。
1.生命周期事件
会话bean的生命周期可以划分为若干阶段或事件。bean生命周期的最明显的两个事件是创建(creation)和销毁(destruction),所有EJB都会经历这两个阶段。会话bean的生命周期从创建bean实例开始,它通常在客户端通过JNDI查找或通过依赖注入得到对bean的引用时发生。实例化bean时会发生如下步骤:
容器调用bean对象的newInstance方法,它在本质上转换为调用bean实现类的构造器。
如果bean使用依赖注入,那么所有对资源、其他bean和环境组件的依赖都被注入到新创建的bean实例中。
当容器确定不再需要实例之后,实例就被销毁。下图描述了这一系列过程。
2.了解生命周期回调
生命周期回调是容器调用来通知bean生命周期转换或事件的bean方法(不通过业务接口暴露)事件发生时,容器调用相应的回调方法,你可以使用这些方法执行业务逻辑或者操作,比如资源的初始化或清理。同调方法是使用元数据注解(比如@PostContruct和@PreDestroy)标记的bean方法。它们可以是public、private、protected或package-protected,当bean实例被创建后和依赖被注入后会立即调用PostContruct回调。 在销毁bean之前调用PreDestroy回调, 它有助于清理bean使用的资源。
所有会话bean都有@PostContruct和@PreDestroy生命周期事件。而有状态会话bean还有另外两个事件:PrePassivate和PostActivate, 因为有状态会话bean维护状态,所以每个客户端都具有有状态会话bean实例,而且容器中可能有很多有状态会话bean的实例。如果出现这种情况, 容器可以决定在不使用时暂时使有状态bean不活动, 这种处理称为钝化。当需要bean实例时,容器再次激活它,这种处理称为激活。@PrePassivate和@PostActivate注解用于钝化和激活生命周期事件。创建生命周期回调是为了处理EJB的生命周期事件, 可以在bean类中或外部拦截器中创建这些回调方法。
无状态bean
无状态bean是池化的。也就是说,对于每个受管理的bean,容器都在池中保持特定数量的实例以便使用。对于每个客户端请求,快速地从池中将实例分配给客户端。当客户端请求结束时,实例退回到池中以便重用。也就是说,少量的bean实例可以为相对大量的客户端提供服务。如图
有状态会话bean
有状态会话bean保证维护会话状态。无状态bean和有状态bean的唯一区别在于容器管理它们的生命周期的方式不同。与无状态bean不同,容器确保同一个客户端发出的后续方法调用由同一个有状态bean实例处理。为在户端保留bean实例并且每个实例存储客户端的状态信息。bean实例在被客户端删除或在它超时之前一直存在。下图显示了容器在幕后强制实施的bean实例和客户端之间的一对一映射。
这种一对一关系有其代价,当客户端会话仍然处于活动状态时,bean实例不能以就绪形式返回池并且重用。bean实例必须保留在内存中,等待拥有会话的客户端发出下一个请求,结果大量并发用户使用的有状态会话bean实例占用了大量内存。这里有一种优化技术——钝化,它可用于缓和这一问题。有状态会话bean非常适用于多步骤的面向工作流的业务处理。
指定业务接口
客户端应用程序可以通过三种不同的方式调用无状态会话bean。除了同一JVM内的本地调用和通过RMI的远程调用之外,还可以像Web服务那样调用无状态bean。三种业务接口类型对应不同的访问类型,不容的注解标识不同的类型,下面详细分析一下这些注解。
1.本地接口
本地接口的目的是让无状态会话bean的客户端在相同的容器(JVM)实例中进行协同操作,通过@Local注解把接口指定为本地业务接口。
@Local
pubhc rnterface B,dManagerLocal
2.远程接口
位于EJB容器的JVM实例之外的客户端必须使用某种类型的远程接口,如果客户端也是使用Java编写的,那么远程EJB访问选择的大多数逻辑和有效资源是Java远程方法调用。它是高度有效的、基于TCP/IP的远程通信API,使跨越网络调用方法所需的大部分工作自动化完成。EBJ支持使用@Remote注解通过RMI访问无状态bean示例中的BidManager业务接口使用此注解使 bean可以被远程访问:
@Remote
public interface BidManager extends Remote
远程业务接口可以扩展java.rmi.Remote,如果bean接口没有扩展它,容器通常在部署过程中执行字节码增强,从而扩展java.rmi. Remote。一般不 要求远程业务接口方法抛出java.rmi.RemoteException异常,除非业务接口扩展 java.rmi.Remote接口。远程业务接口有一个专门要求:接口方法的所有参数和返回类型都必须是Serializable。这是因为,只有Serializable对象可以使用RMI跨越网络传送。
3.Web服务瑞点接口
接口的第三种类型是无状态会话bean专有的,Web服务端点接口(web service endpoint interface,也成SEI)把无状态会话bean暴露为基于SOAP的Web服务的能力是EJB 3最为强大的特性之一。使一个bean的SOAP可被访问所需的全部操作是使用@javax.jws.WebService注解标记业务接口。
@WebService
public interface BidManagerWS
你可以在bean类中使用@Local、@Remotete或@WebService注解,而不必实现业务接口,如下所示:
@Remote(BidManager.class)
@Stateless
public class BidManagerBean
上述代码通过bean类本身将BidManager接口标记为远程的。
有状态会话bean通过@Local和@Remote注解支持本地和远程调用。但是,有状态会话bean不能有Web服务端点接口,因为基于SOAP的web服务实际上是天然无状态的。此外,在有状态bean的业务接口中,永远都应该至少包含一个注解为@Remove的方法。
使用bean生命周期回调
bean中的生命周期回调方法必须遵守void
钝化和激活
如果客户端在足够长的时间内没有调用bean,那么继续使bean存在于内存中就不好了,如果有大量bean,这样做很容易耗尽计算机的内存。容器将利用钝化技术节省内存。
钝化的本质是把bean实例保存到磁盘中,而不是存在于内存中。容器通过串行化整个bean实例并且把它转移到永久存储(比如主件或者数据库)中以完成这个任务。激活是钝化的反向操作,当再次需要bean实例时进行该操作。容器从永久存储中获得bean实例,反串行化它并且把它转换回内存,从而激活bean实例。bean实例变量必须是Java原始类型或实现java.io.Serializable接口。
注意,绝不能把有状态会话bean注入到无状态对象中,比如可能被多个并发客户端共享的无状态会话bean或servlet (这种情况下应该使用JNDI).但是,将无状态会话bean的实例注入到有状态会话bean中是完全合法的。