检验EJB3.0简化API规范

Java™ Community Process(JCP)发布了 JSR 220 下 Enterprise JavaBeans™ (EJB) 3.0 规范的早期第二版草案,它分为三个文件:

EJB 3.0 简化 API:定义了新型的用于代码 Enterprise JavaBean Components 的简化 API ,尤其是会话 bean 和消息驱动 bean。

Enterprise JavaBeans 核心契约和需求:定义了 Bean 和 EJB 容器之间的 EJB 契约。

持久性 API:定义了用于持久性的新实体 Bean 模型。

在本栏中,我觉得我应该突出 EJB 3.0 简化 API 中的一些特性,并且评论一下能够做一些改进的地方。因为我只提出和讨论简化 API,因此当谈到 EJB 组件的时候,我将涉及到会话 bean 和消息驱动 bean,而不是实体 bean,因为在简化 API 下,没有包含持久性。

EJB 规范有很多目标,其中大部分目标我不会涉及到,但是简而言之:EJB 3.0 规范包括了 J2EE™ 5.0 规范的全部目标,其目的是要简化 J2EE 编程模型。正因如此,这部分规范与其说是一套功能性增强技术,还不如说是对原来规范的修改。然而,规范中仍然有许多新编程模型为开发者添加的技术,并且我会像查看某个特性一样谈论它们。

本文假定您已经了解了当前 EJB 规范的一些知识。

POJO 和注解

“POJO” (传统的 Java 对象)是一个最近在很多领域不再使用的术语。它涉及到作为普通 Java 类来编写的代码。因为 EJB 编程可以使您扩展特定的类,可以提供几个接口,并且可以编写部署描述符,所以它们被视为重载的 Java 对象而不再属于普通类。相反,开发者必须要有 J2EE 容器来运行和测试它们。我承认,不使用工具对 POJO 进行编码使得开发很困难。在 EJB 3.0 规范中:

EJB 组件不再需要本地接口。除此之外,EJB 组件没有必要再提供不同的接口或者扩展一些明确的 EJB 特定类别。

J2SE 5.0 注解现在是用于实现组件的一种主要的代理。通过详细说明这些特殊的注解,开发者可以创建 EJB 组件的 POJO 类。该类将通过注解处理器得以运行,并且潜在的容器将会提供管道。

EJB 3.0 引入了业务接口的概念。以下显示了一个实例:

public interface Stock{  public double getQuote(String symbol);}

Your Bean class can implement the interface:

@Stateless public class StockBean implements Stock  public double getQuote(String symbol)  {  return 100.33;  }}

以上的@无状态注解,意味着此类现在是一个无状态会话 bean,将使用业务接口来调用它。早期的草案中允许存在从 Bean 类产生业务接口的可能性。无需实现特定的接口和注解就可以对无状态会话 Bean 进行编码。以下显示了一个实例:

@Stateless public class StockBean  public double getQuote(String symbol)  {  return 100.33;  }}

那么注解处理器将会产生业务接口。在缺省情况下,所有公共的且非注入(injection)的方法都将包括在该业务接口中,除非这些方法是需要特殊注解的。举例来说,如果使用 @BusinessMethod 至少指定一个方法,那么只有那些使用 @BusinessMethod 指定的方法才会包括在业务接口中:

@Stateless public class StockBean  @BusinessMethod public double getQuote(String symbol)  {  return 100.33;  }}

现在我们有一个接口,那么我们如何指定某方法是远程的还是本地的?当然,我们可以使用注解:

@Stateless public class StockBean  @Remote public double getQuote(String symbol)  {  return 100.33;  }}

您可以对该业务接口或者该 Bean 类本身进行注解。当您选择产生该业务接口的时候,使用 Bean 类上的这些注解将非常有用。在该 EJB 规范草案中没有提及如何指定某方法是否应该被作为 Web 服务来调用;相反,这种情况委托给 JSR 181 处理,它定义了用于 Web 服务的注解。

容器服务

EJB 组件之所以流行,这是因为它对事务管理和安全性的隐性支持。EJB 3.0 规范将使用注解来应用容器服务。这里是一个用户如何在无状态会话上指定事务处理的属性的实例:

@Stateless public class StockBean{@TransactionAttribute(TransactionAttributeType.REQUIRESNEW)  public double getQuote(String symbol)  {  return 100.33;  }}

该注解意味着此方法将会在新的事务中运行。阅读此规范以获取关于不同注解的特定语法和语义的详细内容。容器服务同时也可以使用部署描述符得到应用,尽管当前的规范草案中并没有标明如何来指定。然而,如果两种方法都采用的话,部署描述符将会忽略这些注解。

特殊注解的最终形式在未来的规范草案中可以有所改动。例如,总体上与 J2EE 规范相关的确定的注解可能不再属于 EJB。

回调

什么是回调?在 EJB 3.0 规范发布之前,开发者必须在 Bean 类上实现回调方法,例如 ejbCreate();Bean 类必须要实现所有的方法,而不管是否使用它们。在多数情况下,这些方法的实现是空缺的。

现在通过注解来处理回调功能。这里有两种机制:回调方法和回调侦听器类。下面是一个用户如何使用回调方法编写代码来对回调操作做出反应的实例:

@Stateless public class StockBean implements Stock  public double getQuote(String symbol)  {  return 100.33;  }  @PostConstruct initializeCache()  {  }}

以上代码可以使您在创建 bean 实例后实现代码。如果我想要使用回调侦听器,那么我应该创建一个回调侦听器类:

public class MyCallbackListener{  @PrePassivate public clearCache(Object obj)  {  Stock stock = (Stock) obj;  //perform. logic  }}

回调类并不是 Bean 类的一部分,它必须包括 java.lang.Object 参数。那么容器就会传送该 bean 实例。Bean 类通过使用 Bean 类级别上的特定回调注解来添加回调侦听器:

@CallbackListener MyCallbackListener@Stateless public class StockBean implements Stock  public double getQuote(String symbol)  {  return 100.33;  }}

回调功能是很精密的,这是因为在您的代码中包含它们是有条件的,而不像您实现接口时的那种情况。通过添加带有空回调的特定抽象类同样可以完成该操作。在规范的附录 A 中,提到了为那些希望继续使用 EJB 2.x 开发方式的开发着而提供的特定基类。

拦截器

EJB 规范中新添的一个细致功能就是拦截器的使用。可以进行预/后处理的能力已经从 EJB 组件中消失,这点与 servlet 过滤器对 servlet 的操作类似。开发者可以开发拦截器类,并且将其应用到 bean。以下是拦截器审查 StockBean 类的调用的实例:

public class StockRequestAudit {  @AroundInvoke  public Object auditStockOperation(InvocationContext inv) throws  Exception {  try {   Object result = inv.proceed();   Auditor.audit(inv.getMethod().getName(), inv.getParameters[0]);   return result;  } catch (Exception ex) {   Auditor.auditFailure(ex);   throw ex;  }  }}

以上的拦截器截取了对目标 EJB 方法的调用,然后就调用 InvocationContext 上的 proceed() 方法。这样将使得该调用可以通向被调用的实际 EJB 方法。在返回目标 EJB 方法后,它将使用 InvocationTarget 里的元数据来获取被调用的 EJB 组件的方法名称和参数。那么该拦截器就可以应用到 Bean 类:

@Stateless @Interceptors({StockRequestAudit})public class StockBean implements Stock  public double getQuote(String symbol)  {  return 100.33;  }}

除此之外,您可以选择开发那些在 Bean 类内部实现的拦截器方法,同时也可以指定多个拦截器,这种情况下它们被调用的次序由在 Bean 类中定义的次序决定。

引入依赖性

使得 EJB 开发测试操作很困难的原因是 EJB 代码依赖于数据源等因素,这种情况如同 EJB 客户端如何调用 EJB 组件一样。EJB 3.0 规范将引入依赖性作为一种组织机制来介绍,以此来减少这些困难。EJB 能够通过引入代码来定义资源引用,而不是通过使用 JNDI 查看功能。下面是一个 EJB bean 需要调用另一个 EJB 组件并且使用数据源来完成 JDBC 工作的实例:

@Stateless public class StockBean implements Stock{@EJB(name="MarketBean", businessInterface="Market")Market market;@Resource(name="StockDB, resourceType="javax.sql.DataSource")DataSource stockDS   public double getQuote(String symbol)  {  Connection con = stockDS.getConnection();  //DO JDBC work   return market.getCurrentPrice(symbol);  }}

依赖性的引入可以以多种方式出现,例如,通过设置属性值的方法或者类变量。

注解还是部署描述符?

当前 EJB 3.0 规范的早期草案标记出了空缺的章节,将使用部署描述符作为注解的一种备选方案。但是注解方法会更好吗?考虑一下”部署” 描述符的名称,该名称意味着它描述了与部署相关的应用程序的元素。我认为在目前的 EJB 部署描述符中的许多元素都是开发构件,而不是部署构件。那么在部署描述符中什么元素比较好呢?

被部署时很可能改变的元素,例如资源引用到实际资源的映射。

全面应用到 EJB 组件群的元素。注解在为特定类指明元素方面做了很多有益的工作,但是应用到多个 bean 的元素应该在外部得到描述。例如,我想将一个一般的安全角色应用到一套 EJB 组件中,而不是单独的 EJB 组件。为该指定操作留有空间要比在每个单独的 EJB Bean 类上定义注解更有效率。

确定的元素(留心事务划分)应该从部署描述符中移除,这是因为它们与部署无关。允许部署器忽略例如事务语义的行为将非常具有危险。JCP 应该在以后的修改中注意这些事实。

结束语

EJB 规范中简化 API 部分的第二个草案依然非常空洞;该规范的契约部分也已经从早期草案中消失。我期待该核心契约将在很多方面做出定义,例如注解中定义的资源引用如何向定义在应用程序服务器上的实际数据源获取”映射”,定时服务会发生什么行为,等等。不看该核心契约,很难做出评论,因此我们将其留作以后讨论。

今天的开发者应该如何去做呢?继续使用当前的 EJB 规范,用户的 J2EE 应用程序服务器的厂商在很长一段时间内会完全支持该规范。如果您跟随最佳实践,例如对自己的应用程序分层,就可以用最轻松的方式进行修改。

不然你大概会一直好奇和不甘吧——家门前的那条小路,

检验EJB3.0简化API规范

相关文章:

你感兴趣的文章:

标签云: