19.3.使用Spring提供的辅助类实现EJB组件
19.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"> <constructor-arg value="businessApplicationContext.xml" /> </bean> </beans>
上例中,常量ServicesConstants.PRIMARY_CONTEXT_ID
定义如下:
public static final String ServicesConstants.PRIMARY_CONTEXT_ID = "businessBeanFactory";
BeanFactoryLocator
和类ContextSingletonBeanFactoryLocator
的更多使用信息请分别查看他们各自的Javadoc文档。