做为一个轻量级的容器,Spring常被认为EJB是的替代品。我们也相信,对于很多(不一定是绝大多数)应用和情况,相比采用EJB及EJB容器来实现同样的功能,采用Spring作为容器,借助它对事务,ORM和JDBC存取这些领域的良好支持,的确会是一个更好的选择。
不过,需要特别注意的是,使用了Spring并不是说我们就不能用EJB了。实际上,Spring使得访问EJB和实现EJB及其内部功能更加方便。另外,如果通过Spring来访问EJB组件服务,以后就可以在本地EJB组件,远程EJB组件,或者是POJO(简单Java对象)这些变体之间透明地切换实现方式,而不需要改变客户端的代码。
本章,我们来看看Spring是如何帮助我们访问和实现EJB组件的。Spring在访问无状态Session Bean(SLSBs)的时候特别有用,现在我们就由此开始讨论。
为了调用一个本地或者远程无状态session bean上的方法,通常客户端的代码必须进行JNDI查找,获取(本地或远程的)EJB Home对象,然后调用该对象的"create"方法,才能得到实际的(本地或远程的)EJB对象。如此一来,调用的方法比调用EJB组件本身的方法还要多。
为了避免重复的底层代码,很多EJB应用使用了服务定位器(Service Locator)和业务委托(Bussiness Delegate)模式,这样要比在客户端代码中到处都进行JNDI查找要好些,不过它们的常见实现都有严重的缺陷。例如:
-
通常如果代码依赖于服务定位器或业务代理单件来使用EJB,则很难对其进行测试。
-
如果只使用了服务定位器模式而不使用业务委托模式,应用程序代码仍然需要调用EJB Home组件的create方法,并且要处理由此产生的异常。这样代码依然存在和EJB API的耦合并感染了EJB编程模型的复杂性。
-
实现业务委托模式通常会导致大量的冗余代码,因为对于EJB组件的同一个方法,我们不得不编写很多方法去调用它。
Spring的解决方法是允许用户创建并使用只要很少代码量的业务委托代理对象,一般在Spring的容器里配置。不再需要编写额外的服务定位器或JNDI查找的代码,以及手写的业务委托对象里面冗余的方法,除非它们可以带来实质性的好处。
18.2.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容器获得该控制器对象,我们就可以(在同一个上下文中)配置一个LocalStatelessSessionProxyFactoryBean的实例,它将作为EJB组件的代理对象。这个代理对象的配置和控制器属性myComponent的设置是使用一个配置项完成的,如下所示:
<bean id="myComponent"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName" value="myComponent"/>
<property name="businessInterface" value="com.mycom.MyComponent"/>
</bean>
<bean id="myController" class="com.mycom.myController">
<property name="myComponent" ref="myComponent"/>
</bean>
这些看似简单的代码背后有很多复杂的处理,受益于Spring的AOP框架,你甚至不必知道这些概念,就可以享用它的成果。Bean myComponent的定义创建了一个EJB组件的代理对象,它实现了业务方法接口。这个EJB组件的本地Home对象在启动的时候就被放到了缓存中,所以只需要执行一次JNDI查找。每当EJB组件被调用的时候,这个代理对象就调用本地EJB组件的classname方法,并调用EJB组件上相应的业务方法。
在Bean myController的定义中,控制器类的属性myController的值被设置为上述的EJB代理对象。
这种EJB组件访问机制大大简化了应用程序代码:Web层(或其他EJB客户端)的代码不再依赖于EJB组件的使用。如果我们想把这个EJB的引用替换为一个POJO,或者是模拟对象或其他测试桩架,我们只需要简单地修改Bean myComponent的定义而无需修改一行Java代码,此外,我们也不再需要在应用程序中编写任何JNDI查找或其它EJB相关的代码。
真实应用中的评测和经验表明,这种方法(尽管其中使用了反射来调用目标EJB组件的方法)的性能开销是最小的,通常使用中几乎觉察不出。虽然如此,仍请牢记我们不要过多地调用EJB组件,因为应用服务器中EJB的基础框架毕竟会带来性能损失。
关于JNDI查找有一点需要特别注意。在一个Bean容器中,这个类通常最好用作单件(没理由使之成为原型)。不过,如果这个Bean容器会预先实例化单件(XML ApplicationContext的几种变体就会这样),并在EJB容器加载目标EJB前被加载,我们就可能会遇到问题。因为JNDI查找会在该类的init方法中被执行然后缓存结果,但是此时EJB还没有被绑定到目标位置。解决方案就是不要预先实例化这个工厂对象,而让它在第一次被用到的时候再创建,在XML容器中,这是通过属性lazy-init来控制的。
尽管大部分Spring的用户不会对这些感兴趣,但那些对EJB进行AOP编程工作的用户则想看看LocalSlsbInvokerInterceptor。
基本上访问远程EJB与访问本地EJB差别不大,只是前者使用的是SimpleRemoteStatelessSessionProxyFactoryBean。当然,无论是否使用Spring,远程调用的语义都相同;对于使用场景和错误处理来说,调用另一台计算机上不同虚拟机中对象的方法和本地调用会有所不同。
与不使用Spring方式的EJB客户端相比,Spring的EJB客户端有一个额外的好处。通常如果要想能随意的在本地和远程EJB调用之间切换EJB客户端代码,是会产生问题的。这是因为远程接口的方法需要声明他们抛出的RemoteException方法,然后客户端代码必须处理这种异常,但是本地接口的方法却不需要这样。如果要把针对本地EJB的代码改为访问远程EJB,就需要修改客户端代码,增加处理远程异常的代码,反之要么保留这些用不上的远程异常处理代码要么就需要进行修改以去除这些异常处理代码。使用Spring的远程EJB代理,我们就不再需要在业务方法接口和EJB的实现代码中声明要抛出的RemoteException,而是定义一个相似的远程接口,唯一不同就是它抛出的是RemoteException, 然后交给代理对象去动态的协调这两个接口。也就是说,客户端代码不再需要与 RemoteException这个checked exception打交道,实际上在EJB调用中被抛出的RemoteException都将被以unchecked exception RemoteAccessException的方式重新抛出,它是RuntimeException的一个子类。这样目标服务就可以在本地EJB或远程EJB(甚至POJO)之间随意地切换,客户端不需要关心甚至根本不会觉察到这种切换。当然,这些都是可选的,没有什么阻止你在你的业务接口中声明RemoteExceptions异常。
18.3. 使用Spring提供的辅助类实现EJB组件
Spring也提供了一些辅助类来为EJB组件的实现提供便利。它们是为了倡导一些好的实践经验,比如把业务逻辑放在在EJB层之后的POJO中实现,只把事务划分和远程调用这些职责留给EJB。
要实现一个无状态或有状态的Session Bean,或消息驱动Bean,你只需要从AbstractStatelessSessionBean、AbstractStatefulSessionBean和AbstractMessageDrivenBean/AbstractJmsMessageDrivenBean分别继承你的实现类。
考虑这个无状态Session bean的例子:实际上我们把无状态Session Bean的实现委托给一个普通的Java服务对象。业务接口的定义如下:
public interface MyComponent {
public void myMethod(...);
...
}
这是简单Java对象的实现:
public class MyComponentImpl implements MyComponent {
public String myMethod(...) {
...
}
...
}
最后是无状态Session Bean自身:
public class MyComponentEJB extends AbstractStatelessSessionBean
implements MyComponent {
MyComponent myComp;
/**
* Obtain our POJO service object from the BeanFactory/ApplicationContext
* @see org.springframework.ejb.support.AbstractStatelessSessionBean#onEjbCreate()
*/
protected void onEjbCreate() throws CreateException {
myComp = (MyComponent) getBeanFactory().getBean(
ServicesConstants.CONTEXT_MYCOMP_ID);
}
// for business method, delegate to POJO service impl.
public String myMethod(...) {
return myComp.myMethod(...);
}
...
}
缺省情况下,Spring EJB支持类的基类在其生命周期中将创建并加载一个Spring IoC容器供EJB使用(比如像前面获得POJO服务对象的代码)。加载的工作是通过一个策略对象完成的,它是BeanFactoryLocator的子类。默认情况下,实际使用的BeanFactoryLocator的实现类是ContextJndiBeanFactoryLocator,它根据一个被指定为JNDI环境变量的资源位置来创建一个ApplicationContext对象(对于EJB类,路径是 java:comp/env/ejb/BeanFactoryPath)。如果需要改变BeanFactory或ApplicationContext的载入策略,我们可以在 setSessionContext()方法调用或在具体EJB子类的构造函数中调用setBeanFactoryLocator()方法来覆盖默认使用的 BeanFactoryLocator实现类。具体细节请参考JavaDoc。
如JavaDoc中所述,有状态Session Bean在其生命周期中将会被钝化并重新激活,由于(一般情况下)使用了一个不可串行化的容器实例,不可以被EJB容器保存,所以还需要手动在ejbPassivate和ejbActivate 这两个方法中分别调用unloadBeanFactory()和loadBeanFactory,才能在钝化或激活的时候卸载或载入。
有些情况下,要载入ApplicationContext以使用EJB组件,ContextJndiBeanFactoryLocator的默认实现基本上足够了,不过,当ApplicationContext需要载入多个bean,或这些bean初始化所需的时间或内存很多的时候(例如Hibernate的SessionFactory的初始化),就有可能出问题,因为每个EJB组件都有自己的副本。这种情况下,用户会想重载ContextJndiBeanFactoryLocator的默认实现,并使用其它 BeanFactoryLocator的变体,例如ContextSingletonBeanFactoryLocator ,他们可以载入并在多个EJB或者其客户端间共享一个容器。这样做相当简单,只需要给EJB添加类似于如下的代码:
/**
* Override default BeanFactoryLocator implementation
* @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext)
*/
public void setSessionContext(SessionContext sessionContext) {
super.setSessionContext(sessionContext);
setBeanFactoryLocator(ContextSingletonBeanFactoryLocator.getInstance());
setBeanFactoryLocatorKey(ServicesConstants.PRIMARY_CONTEXT_ID);
}
然后需要创建一个名为beanRefContext.xml的bean定义文件。这个文件定义了EJB中所有可能用到的bean工厂(通常以应用上下文的形式)。许多情况下,这个文件只包括一个bean的定义,如下所示(文件businessApplicationContext.xml包括了所有业务服务POJO的bean定义):
<beans>
<bean id="businessBeanFactory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg value="businessApplicationContext.xml" />
</bean>
</beans>
上例中,常量ServicesConstants.PRIMARY_CONTEXT_ID定义如下:
public static final String ServicesConstants.PRIMARY_CONTEXT_ID = "businessBeanFactory";
BeanFactoryLocator和类ContextSingletonBeanFactoryLocator的更多使用信息请分别查看他们各自的Javadoc文档。
相关推荐
Enterprise Java Bean(EJB)是Java平台上用于构建可部署在企业级服务器上的分布式应用程序的核心技术。EJB提供了组件模型,使开发人员能够专注于业务逻辑,而无需关心底层基础设施的复杂性,如事务管理、安全性和...
**EJB**(Enterprise JavaBeans)是一种用于开发企业级应用的标准,由JCP(Java Community Process)制定,属于Java EE平台的一部分。EJB允许开发者创建可复用的企业级组件,这些组件可以在支持EJB的容器中运行。 **1.1...
Enterprise JavaBean(EJB)是Java平台上用于构建可部署到企业级服务器的组件模型。它是一种规范,由Java Community Process(JCP)定义,旨在简化企业级应用开发,特别是分布式计算环境中的应用。EJB技术提供了面向...
本系统采用Java Bean、Enterprise JavaBeans (EJB) 和JavaServer Pages (JSP) 技术构建,具备发送邮件功能,并能与MySQL数据库进行连接。下面我们将详细探讨这些技术以及它们在用户注册系统中的应用。 **Java Bean*...
"基于Java的实例开发源码-Message-Driven Bean EJB实例源代码.zip" 这个标题揭示了本次讨论的核心内容,即一个关于Java编程的实例项目,特别是涉及了Java企业版(Java EE)中的Message-Driven Bean(MDB)组件。...
1. **Java EE(Java Platform, Enterprise Edition)基础**:Java EE是企业级应用开发的标准框架,提供了诸如Servlet、JSP、EJB等组件模型,用于构建可扩展、高性能和安全的服务器端应用。 2. **Servlet技术**:...
Java中的Message-Driven Bean (MDB) 是Enterprise JavaBeans (EJB) 规范的一部分,用于处理JMS(Java Message Service)消息。这个压缩包“基于Java的源码-Message-Driven Bean EJB实例源代码.zip”显然包含了实现...
EJB(Enterprise JavaBeans)是Java EE的高级组件模型,包括会话bean(Session Beans)、实体bean(Entity Beans)和消息驱动bean(Message-Driven Beans),用于处理事务、持久化和并发等企业级功能。 4. **JMS与...
3. **EJB**:EJB提供了一种标准的框架来创建可部署在Java EE服务器上的企业级组件,分为会话bean(Session Beans)、实体bean(Entity Beans)和消息驱动bean(Message-Driven Beans),分别处理业务逻辑、持久化...
在Java企业级开发中,Enterprise JavaBeans(EJB)是一种核心组件,用于构建分布式、事务处理和安全的企业级应用程序。EJB3是EJB规范的一个版本,它引入了许多简化开发的特性,使得EJB更加易于使用。本文档主要介绍...
本篇文章将深入探讨EJB实体Bean的概念、其在Eclipse集成开发环境中的使用,以及与MySQL数据库的集成。 ### 一、EJB实体Bean概述 EJB实体Bean是Java EE应用服务器管理的对象,它们负责处理数据存储和检索。实体Bean...
EJB(Enterprise JavaBeans)是Java EE平台的一部分,用于开发和部署可重用的服务组件。EJB规范定义了如何编写、部署以及管理企业级应用程序组件的标准。它允许开发者创建组件模型来构建高度可伸缩的、健壮的企业...
Enterprise Java Beans(EJB)是Java平台上用于构建可部署在企业级服务器上的分布式应用程序的核心技术。EJB标准由Java Community Process(JCP)制定,它为开发者提供了在服务器端进行业务逻辑处理的能力,并且提供...
### Hibernate与EJB集成 #### 一、简介 在企业级应用开发中,EJB(Enterprise JavaBeans)和Hibernate都是重要的技术。EJB是Java EE平台的核心组成部分之一,主要用于实现分布式计算环境下的重用性和可扩展性。而...
消息驱动Bean(Message-Driven Bean,MDB)是Java企业版(Enterprise JavaBeans,EJB)规范中的一个重要组件,主要用于处理消息队列中的消息。在Java应用程序中,MDB扮演着消费者的角色,它监听并消费JMS(Java ...
4. **EJB(Enterprise JavaBeans)**:书中详细介绍了EJB的设计模式和最佳实践,包括会话bean、实体bean和消息驱动bean的使用,以及如何处理并发和持久化问题。 5. **Web服务和SOA**:在Java EE框架下,作者讨论了...
EJBQL (Enterprise JavaBeans Query Language) 是一种查询语言,用于在EJB组件内部查询数据。这一部分将介绍如何使用EJBQL查询数据。 **开发EJB2.0的JMS** JMS (Java Message Service) 是Java平台上消息传递的标准...
3. **Entity Beans 3.0(EJB 3.0引入)**:简化了Entity Bean的使用,采用了注解(Annotations)方式,使得持久化过程更为简洁,与JPA(Java Persistence API)紧密集成。 **EJB的生命周期** 无论是Session Bean...
### JavaEE5学习笔记07-Web层与EJB集成总结 #### 一、JavaEE5框架中的Web层与EJB集成概述 在JavaEE5(即Java Platform, Enterprise Edition 5)中,Web层与EJB(Enterprise JavaBeans)的集成是构建企业级应用的...
Java企业版(Java EE,以前称为J2EE)是一个用于构建企业级应用程序的框架,它包含了一系列组件和服务,如JavaBeans、Servlet、JSP、EJB(Enterprise JavaBeans)。EJB是Java EE的核心部分,主要用于处理分布式计算...