为轻量级的容器,Spring常常被认为是EJB的替代品。我们也相信,对于很多 (不一定是绝大多数)应用和用例,相对于通过EJB容器来实现相同的功能而言, Sping作为容器,加上它在事务,ORM和JDBC存取这些领域中丰富的功能支持, Spring的确是更好的选择。
不过,需要特别注意的是,使用了Spring并不是说我们就不能用EJB了, 实际上,Spring大大简化了从中访问和实现EJB组件或只实现(EJB组件)其功能的复杂性。 另外,如果通过Spring来访问EJB组件服务,以后就可以在本地EJB组件,远程EJB组件, 或者是POJO(简单Java对象)这些变体之间透明地切换服务的实现,而不需要修改 客户端的代码。
本章,我们来看看Spring是如何帮助我们访问和实现EJB组件的。尤其是在访问 无状态Session Bean(SLSBs)的时候,Spring特别有用,现在我们就由此开始讨论。
访问EJB
1.1. 概念
要调用本地或远程无状态Session Bean上的方法,通常客户端的代码必须 进行JNDI查找,得到(本地或远程的)EJB Home对象,然后调用该对象的"create" 方法,才能得到实际的(本地或远程的)EJB对象。前后调用了不止一个EJB组件 上的方法。
为了避免重复的底层调用,很多EJB应用使用了服务定位器(Service Locator) 和业务委托(Bussiness Delegate)模式,这样要比在客户端代码中到处进行JNDI 查找更好些,不过它们的常见的实现都有明显的缺陷。例如:
通常,若是依赖于服务定位器或业务代理单件来使用EJB,则很难对代码进 行测试。
在仅使用了服务定位器模式而不使用业务委托模式的情况下,应用程序 代码仍然需要调用EJB Home组件的create方法,还是要处理由此引入的异常。 导致代码仍然保留了与EJB API的耦合性以及EJB编程模型的复杂性。
实现业务委托模式通常会导致大量的冗余代码,因为我们不得不编写 很多方法,而它们所做的仅仅是调用EJB组件的同名方法。
Spring采用的方法是允许创建并使用代理对象,一般是在Spring的 ApplicationContext或BeanFactory里面进行配置,这样就和业务代理类似,只需要 少量的代码。我们不再需要另外编写额外的服务定位器或JNDI查找的代码,或者是手写 的业务委托对象里面冗余的方法,除非它们可以带来实质性的好处。
1.2. 访问本地的无状态Session Bean(SLSB)
假设有一个web控制器需要使用本地EJB组件。我们遵循前人的实践经验, 于是使用了EJB的业务方法接口(Business Methods Interface)模式,这样, 这个EJB组件的本地接口就扩展了非EJB特定的业务方法接口。让我们假定这个 业务方法接口叫MyComponent。
public interface MyComponent {
...
}
(使用业务方法接口模式的一个主要原因就是为了保证本地接口和bean的实现类 之间方法签名的同步是自动的。另外一个原因是它使得稍后我们改用基于POJO(简单Java对象) 的服务实现更加容易,只要这样的改变是有利的。当然,我们也需要实现 本地Home接口,并提供一个Bean实现类,使其实现接口SessionBean和业务方法接口 MyComponent。现在为了把我们Web层的控制器和EJB的实现链接起来,我们唯一要写 的Java代码就是在控制器上公布一个形参为MyComponent的setter方法。这样就可以 把这个引用保存在控制器的一个实例变量中。
private MyComponent myComponent;
public void setMyComponent(MyComponent myComponent) {
this.myComponent = myComponent;
}
然后我们可以在控制器的任意业务方法里面使用这个实例变量。假设我们现在 从Spring的ApplicationContext或BeanFactory获得该控制器对象,我们就可以在 同一个上下文中配置一个LocalStatelessSessionProxyFactoryBean 的实例,它将作为EJB组件的代理对象。这个代理对象的配置和控制器的属性 myComponent的设置是使用一个配置项完成的,如下所示: