Java
许多开发人员都对企业 JavaBean (EJB) 的复杂性提出了批评。EJB 3.0(由 Java Community Process 的 JSR-220 开发)旨在简化应用程序的开发并针对 Java 平台完成持久性 API 的标准化。
本文将介绍 EJB 3.0 — Oracle 应用服务器 EJB 3.0 预览版提供了它的完整实现 — 如何简化 EJB 的编程模型并定义一个简单的 API 以实现持久性。
当前的 EJB 2.x 模型由于各种原因而过于复杂:
如果您曾使用当前规范开发过 EJB,那么将了解到开发一个简单 EJB(如 HelloWorld EJB)是也是极其困难的。即使对于这个简单的任务,您也至少需要两个接口:一个 bean 类和一个部署描述符。诸如 Oracle JDeveloper 这样的 IDE 以及类似 XDoclet 这样的实用程序简化了这些普通任务;但开发人员仍需要负责编译这些类并将部署描述符打包,才可以将 EJB 部署到选择的容器中。
EJB 3.0 旨在解决当前 EJB 模型的复杂性,具体体现在:
EJB 3.0 在很大程度上依赖元数据批注。元数据批注由 JSR 175 进行了标准化并且是 J2SE 5.0 的一部分。 批注是面向属性编程方法的一种,并且在功能上类似于 Xdoclet;但与 XDoclet 不同,批注在编译时由 Java 编译器编译到类中(取决于 @Retention 的设置)。总地说来,EJB 3.0 的目标是通过支持元数据批注以生成多个工件(如接口)并替换部署描述符,进而简化开发。
开发人员将批注看作是修饰符(就像 public 一样),并且可以在类、字段、方法、参数、本地变量、构造符、枚举和程序包中使用。你可以在 Java 代码中使用批注,指定可用于生成代码的属性、编写代码或提供特殊服务(如增强的业务级别的安全性或运行时期间的特殊业务逻辑)。如果要了解有关批注的详细信息,可以参阅由 Jason Hunter 编写的 充分利用 Java 的元数据。
J2EE 5.0 的主要目标之一是使用批注简化开发,因此正如您可能期望的,它包含自身的批注集。批注按如下方式使用 @ 进行标记:
@Author("Debu Panda")
@Bean
public class MySessionBean
在典型的术语中,JavaBean 和接口通常分别称作普通旧式 Java 对象 (POJO) 和普通旧式 Java 接口 (POJI)。EJB 类和接口现在分别类似于 POJO 和 POJI。现在已经消除了对类似主接口这样的不必要工件的需求。
您不必在 javax.ejb 程序包中实现某个 EJB 接口( SessionBean、EntityBean 或 MessageDrivenBean)。相反,您可以使用 Stateless、Stateful、MessageDriven 或 Entity 对它们的 bean 类进行批注。例如,如果要将无状态 EJB 定义为 HelloWorld,应按如下所示定义 EJB:
import javax.ejb.Stateless;
@Stateless
public class HelloWorldBean implements HelloWorld
{
public void sayHello(String name)
{
System.out.println("Hello "+name +" from your first EJB 3.0 component ...");
}
}
EJB 的接口(无论是远程接口还是本地接口)不必实现 EJBObject 或 EJBLocalObject。 在本示例中,bean 类为 EJB 实现了业务接口 在本示例中, HelloWorldBean 实现了 HelloWorld 接口。
import javax.ejb.Remote;
@Remote
public interface HelloWorld
{
public void sayHello(String name);
}
如果查看以上的代码示例,则可以非常清楚地看到 @Remote 用于将接口标记为远程接口。如果需要,您的 EJB 可以拥有远程接口和本地接口。主接口现在即使对会话 Bean 而言也是可选的。
从以上示例中,可以清楚地看到,使用 EJB 3.0,许多常规任务(如创建部署描述符和实现不必要的回调方法)现在都已经废弃了。
EJB 2.1 和之前的版本需要为每个 EJB 实现多个生命周期方法(如 ejbPassivate、ejbActivate、ejbLoad、ejbStore 等),尽管从功能方面而言它们并不是必需的。例如,无状态会话 bean 不需要 ejbPassivate;但您需要在 bean 类中实现该方法。由于 EJB 3.0 现在类似于常规 Java 类,因此可以这些生命周期方法实现是可选的。您现在可以在 bean 类中定义生命周期回调方法或将这些方法定义为外部类。可以按以下示例所示使用批注将任意方法标记为回调方法:
@PostConstruct
public void initialize() {
items = new ArrayList();
}
拦截器对方法调用流提供了细粒度控制。可以在无状态会话 bean、有状态会话 bean 和消息驱动 bean 上使用它们。拦截器可以是同一 bean 类中的方法或是一个外部类。
如前所述,在 EJB 3.0 中,批注将替换部署描述符。部署描述符中的每个属性都有缺省值,因此您不必指定这些属性,除非它们需要缺省值以外的值。可以使用 bean 类本身中的批注指定这些值。
EJB 3.0 规范还定义了一组元数据批注,如 bean 类型、接口类型、资源引用、事务属性、安全性等。例如,如果要定义特殊 EJB 方法的安全设置,可以在 bean 类中定义以下内容:
@MethodPermissions("user")
public void sharedTask() {
System.out.println("Shared admin/user method called");
}
这些讨厌的 XML 描述符现在已经被清除!
CMP 实体 bean 已在 EJB 3.0 中进行重要改进,以取得开发人员的青睐:EJB 3.0 当前采用一个轻型持久性模型,如 Oracle TopLink 和 Hibernate。
实体 bean 现在是 POJO,并且不需要组件接口。实体 bean 现在支持继承和多态性。
以下是一个非常简单的实体 bean 的源代码:
@Entity
@Table(name = "EMP")
public class Employee implements java.io.Serializable
{
private int empNo;
private String eName;
private double sal;
@Id
@Column(name="EMPNO", primaryKey=true)
public int getEmpNo()
{
return empNo;
}
public void setEmpNo(int empNo)
{
this.empNo = empNo;
}
public double getSal()
{
return sal;
}
...
}
如果您看过代码后,您将会发现 bean 类是一个具体类,而不是一个抽象类(如 CMP 2.x 实体 bean)。
以下是对实体 bean 所做的一些主要增强:
通过对象关系映射批注的元数据,用户可以使用对象关系映射元数据“修饰”它们的 EJB 3.0 实体 bean 类。随后,此元数据用于定义其实体 bean 的持久性和检索。也就是说,您不必在供应商专有的描述符中定义对象关系映射。
以上示例使用 @Table 和 @Column 批注为实体 bean 指定基础数据库表和列名称。还可以使用映射批注定义实体之间的关系。例如:
@ManyToOne(cascade=PERSIST, fetch=LAZY)
@JoinColumn(name="MANAGER_ID", referencedColumnName="EMP_ID")
public Employee getManager() {
return manager;
}
有关可供您使用的映射批注列表,请参阅 如何映射批注。
继承在很多情况下非常有用。Oracle 应用服务器 EJB 3.0 预览版通常使用和支持的两类继承包括:
继承使用批注表示。以下是一个使用连接的子类方案的示例代码:
@Entity
@Table(name="EJB_PROJECT")
@Inheritance(strategy=JOINED, discriminatorValue="P")
@DiscriminatorColumn(name="PROJ_TYPE")
public class Project implements Serializable {...}
@Entity
@Table(name="EJB_LPROJECT")
@Inheritance(discriminatorValue="L")
public class LargeProject extends Project {
...
}
@Entity
@Table(name="EJB_PROJECT")
@Inheritance(discriminatorValue="S")
public class SmallProject extends Project
{
...
}
简化实体 Bean 的 CRUD 操作的 API
javax.persistence.EntityManager API 用于创建、查找、更新和删除实体 bean 实例。您不必再编写复杂代码以查找实体 bean 实例以及操作它们。 您可以将 EntityManager 的实例注入到会话 bean 中,并在 EntityManager 中使用持久或查找方法以创建或查询实体 bean 对象,如以下代码所示:
@Inject
private EntityManager em;
private Employee emp;
public Employee findEmployeeByEmpNo(int empNo)
{
return ((Employee) em.find("Employee",empNo));
}
public void addEmployee(int empNo, String eName, double sal)
{
if (emp == null) emp = new Employee();
emp.setEmpNo(empNo);
.......
em.persist(emp);
}
}
即使在同一应用程序中对 EJB 进行了配置,使用 EJB 也非常复杂。例如,如果要从其他 EJB 中使用 EJB 2.x,则必须在部署描述符中定义 ejb-ref 或 ejb-local-ref、查找 EJB、创建实例,然后调用方法。而使用 EJB 3.0,您只需使用批注定义与 bean 的相关性并按如下所示调用此方法:
@EJB AdminService bean;
public void privilegedTask() {
bean.adminTask();
}
可以使用相关性注入查找任何类型的环境和资源引用,如数据源、JMS、Web 服务等。
对于依赖 EJB 2.1 规范的开发人员而言,测试就像一场噩梦。开发和测试 EJB 需要 EJB 容器,而且您必须熟悉最终的部署平台以执行测试。
幸运地是,EJB 3.0 允许您在容器外部测试 EJB。Oracle 应用服务器 EJB 3.0 预览版附带的 实体测试工具实现了对 EJB 3.0 进行测试。
对企业 Java 开发人员而言,EJB 3.0 非常有前景。EJB 3.0 规范中提供的建议将有于助消除每个开发人员所面临的复杂性。 Oracle 应用服务器 EJB 3.0 预览版包含 EJB 3.0 Early Release Draft2 规范的完整实现,您可以立即开始构建您的 EJB 3.0 应用程序!
参考资料和其他读物
Debu Panda 是 Oracle 应用服务器开发小组的首席产品经理,并致力于 EJB 容器和事务管理器。他在超过 13 年的 IT 业从业经历,曾在多家杂志发表过文章,并曾出席多场技术会议。您可以在 http://radio.weblogs.com/0135826/ 中找到他编写的以 J2EE 为主的网志。