EJB 3.0 的三大类型

    无论你创建bean会话是为了执行特定任务,还是把表映射到实体bean以更新数据,都可以使用普通的Java对象和接口来完成这些工作,并且可以通过在业务方法中使用注释,把方法提供给客户端。

Enterprise JavaBean (EJB) 是可重用的、可移植的 J2EE 组件。EJB 由封装业务逻辑的方法组成。譬如说,EJB 可能有这样的业务逻辑:包含了更新数据库中客户数据的方法。众多远程和本地客户端可以调用该方法。另外,EJB 在容器里面运行,这样开发人员只要关注bean里面的业务逻辑,不必担心复杂、容易出错的问题,譬如事务支持、安全性和远程对象访问等。EJB 作为普通Java对象(POJO)的形式来开发,开发人员可以使用元数据注释(metadata annotations)来指定容器如何管理这些bean。

EJB 包括三种主要类型:会话 bean、实体 bean和消息驱动的bean。会话bean执行独立的、解除耦合的任务,譬如检查客户的信用记录。实体bean是一个复杂的业务实体,它代表数据库中存 在的业务对象。消息驱动的bean用于接收异步JMS 消息。下面,让我们进一步研究EJB 3.0规范中的这些类型。

会话bean

会话bean通常代表业务流程里面的操作,譬如“处理订单”。可根据对话状态的保持性,即有状态和无状态对会话bean进行分类。

无 状态的会话 bean没有内部状态。它们不跟踪记录从一个方法调用传递到另一个方法调用的信息。因此,每次调用无状态的业务方法都独立于前一次调用,譬如计算税款或者 运费。用某个应税值调用计算税款的方法时,对税款值进行计算并返回给调用方法,而不必保存调用者的内部状态供以后调用。因为这些bean并不保持状态,所 以容器对它们进行管理就很简单。客户端请求无状态的bean实例时,可以从容器保持的无状态的会话bean 实例池当中接收一个实例。另外,因为无状态的会话 bean可以共享,所以容器可保持数量较少的实例为许多客户端提供服务。想指定Java Bean作为无状态的会话bean加以部署及管理,只需要为该bean添加注释@Stateless。

有状态 的会话 bean在方法调用时可保持对话状态,譬如客户的网上购物车。客户开始网上购物时,可以从数据库中检索客户的详细信息。客户往购物车里面添加商品或者从里 面删除商品、下订单等时调用的其他方法也可以使用这些详细信息。不过,有状态的会话bean是暂时性的,因为出现会话终止、系统崩溃或者网络故障后,状态 不复存在。客户端请求有状态的会话bean实例时,就为该客户端分配一个有状态的实例,并为该客户端保持该组件的状态。要指定容器在某个方法完成后删除有 状态的会话bean实例,只要为该方法添加注释@Remove。

会话 bean示例如下:

import javax.ejb.Stateless.*;

  /*A simple stateless session bean implementing the incrementValue() method of the * CalculateEJB interface.*/

@Stateless(name="CalculateEJB")

public class CalculateEJBBean

implements CalculateEJB

{

int value = 0;

  public String incrementValue()

  {

value++;

   return "value incremented by 1";

  }

}

实体bean

实 体bean是管理持久性数据的一个对象,有可能使用几个相关的Java对象,并可以通过主键实现惟一性。通过添加@Entity注释,可以把某类指定为实 体bean。实体bean代表数据库中的持久性数据,如客户表中的一行或者员工表中的一条员工记录。实体bean还可以在多个客户端之间共享。譬如说,某 个员工实体bean可以由多个客户端用于计算某员工的年薪或者更新员工地址。实体bean对象的特定字段可以成为持久性字段。实体bean中没有被 @Transient注释标记的所有字段都被视为持久性字段。EJB 3.0的一个主要特性就是,能够使用元数据注释来创建包含对象/关系映射的实体bean。譬如说,想指定把实体bean的empId字段映射到 Employees表中的EMPNO属性,就要使用@Table(name="Employees") 来注释表名,使用 @Column(name="EMPNO") 来注释字段,如下面的例子所示。另外,EJB 3.0 的一个特性是,在开发期间可以方便地测试实体bean,因为现在使用 Oracle 应用服务器实体测试工具,就可以在容器外面运行实体bean。

实体 bean示例如下:

  import javax.persistence.*;

  import java.util.ArrayList;

  import java.util.Collection;

  @Entity

  @Table(name = "EMPLOYEES")

  public class Employee implements java.io.Serializable

  {

   private int empId;

   private String eName;

   private double sal;

   @Id

   @Column(name="EMPNO", primaryKey=true)

   public int getEmpId()

    { return empId;}

   public void setEmpId(int empId)

    { this.empId = empId; }

   public String getEname()

    { return eName; }

   public void setEname(String eName)

    { this.eName = eName; }

   public double getSal()

    { return sal; }

   public void setSal(double sal)

    { this.sal = sal; }

   public String toString()

    {StringBuffer buf = new StringBuffer();

    buf.append("Class:")

     .append(this.getClass().getName()).append(" ::").append(" empId:").append(getEmpId()).append(" ename:").append(getEname()).append("sal:").append(getSal());

    return buf.toString();}

  }

消息驱动的bean

消 息驱动的bean(MDB)为实现异步通信提供了一种比使用直接的Java消息服务(JMS)更简单的方法。MDB用于接收异步JMS消息。容器处理 JMS队列和主题所需的大部分设置进程。它把所有消息发送给相关的MDB。MDB允许J2EE应用程序发送异步消息,随后这些消息由应用程序来处理。要把 bean指定为MDB,需要实现javax.jms.MessageListener接口,并且用@MessageDriven注释该bean。

消息驱动的bean示例如下:

  import javax.ejb.MessageDriven;

  import javax.ejb.ActivationConfigProperty;

  import javax.ejb.Inject;

  import javax.jms.*;

  import java.util.*;

  import javax.ejb.TimedObject;

  import javax.ejb.Timer;

  import javax.ejb.TimerService;

  @MessageDriven(

  activationConfig = {

  @ActivationConfigProperty(propertyName="connectionFactoryJndiName", propertyValue="jms/TopicConnectionFactory"),

  @ActivationConfigProperty(propertyName="destinationName", propertyValue="jms/myTopic"),

  @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Topic"),

  @ActivationConfigProperty(propertyName="messageSelector", propertyValue="RECIPIENT = 'MDB'") } )

   /** A simple Message-Driven Bean that listens to the configured JMS Queue or Topic and gets notified via an * invocation of it's o­nMessage() method when a message has been posted to the Queue or Topic.The bean

  * prints the contents of the message. */

  public class MessageLogger implements MessageListener, TimedObject

  {

   @Inject javax.ejb.MessageDrivenContext mc;

   public void o­nMessage(Message message)

   { System.out.println("onMessage() - " + message);

     try

     {

       String subject = message.getStringProperty("subject");

       String inmessage = message.getStringProperty("message");

        System.out.println("Message received/n/tDate:" + new java.util.Date() + "/n/tSubject:" + subject + "/n/tMessage:" + inmessage + "/n");

       System.out.println("Creating Timer a single event timer");

       TimerService ts = mc.getTimerService();

       Timer timer = ts.createTimer(30000, subject);

       System.out.println("Timer created by MDB at:" + new Date(System.currentTimeMillis()) +" with info:"+subject); }

   catch (Throwable ex)

   { ex.printStackTrace(); }

  }

   public void ejbTimeout(Timer timer)

   { System.out.println("EJB 3.0:Timer with MDB");

     System.out.println("ejbTimeout() called at:" + new Date(System.currentTimeMillis()));

     return; }

  }

使用 EJB 3.0

EJB 客户端是访问bean的应用程序。它不必位于客户端层上,但可以是独立的应用程序、Java服务器页面(JSP)、服务器小程序或者另一个EJB。客户端 通过bean的远程或本地接口来使用EJB的方法,远程还是本地取决于客户端是在同一个JVM里面还是不同的JVM里面。这些接口定义了bean的方法, 而bean类负责实际实现这些方法。客户端访问bean类的方法时,容器就会为bean生成一个代理,名为远程或者本地对象。远程或者本地对象接收请求 后,交给相应的bean实例,并将结果返回给客户端。想调用bean的方法,客户端需要通过使用EJB部署描述符(deployment descriptor)里面定义的bean名称来找到该bean。在以下的示例中,客户端使用Context对象找到名为“Statelessejb”的 bean。

EJB 客户端示例如下:

  import javax.naming.Context;

  import javax.naming.InitialContext;

  /* A simple bean client which calls methods o­n a stateless session bean.*/

  public class CalculateejbClient

  {

    public static void main(String [] args)

   {

     Context context = new InitialContext();

     CalculateEJB myejb =(CalculateEJB)context.lookup("java:comp/env/ejb/CalculateEJB");

   myejb.incrementValue(); }

  }

链接:必要的准备

EJB 3.0极其重视开发的简易性,并调整了模型。这绝非巧合,因为规范的主要设计者:Linda DeMichiel选择了广泛听取外界的意见,并借鉴TopLink等产品所取得的经验。这样一来,这项规范就可以沿着已经由流行、得到公认的技术开辟出 来的道路前进,而这些道路实际上成了业内事实上的最佳实践。

那么,作为程序员的你,面对新的规范,该做哪些准备呢?

处理好架构问题

首 先要确保你的架构可以利用持久性方面的标准及认可的设计模式。实际上,这可能需要改动你的应用程序,不过如果你期望应用程序能经得起时间的考验,那么进行 这种投入是值得的。使用会话外观、数据访问对象(DAO)层或者服务层总是好主意,不过在这里它们都至关重要。如果你的应用程序已经使用远程实体构建而成 ——虽然这种做法并不常见,那么就需要重新设计架构。访问持久性对象之前,应当先部署可远程化服务层。如果要使用实体,它就应当完全是本地实体。

不过,使用本地实体不是目的,因为实体还为部署人员提供了为实体声明事务和安全需求的功能。EJB 3.0不允许任何这些属性在实体层面进行设定。相反,实体的运行环境将由调用者来确定,所以所需的任何事务或者安全环境将由负责封闭的J2EE组件来安装或者声明。

CMP应用程序

如 果你已经是容器管理持久性(CMP)用户,那么你可能迫不及待地想获得新特性,希望抛弃无关的接口、不必要的bean代码以及繁琐的XML部署描述符,这 些是与以前的实体bean开发相关的一些烦人问题。分别要扩展EJBObject和EJBLocalObject的远程和本地接口再也不需要了;现在实体 只要实现普通Java接口(POJI)即可,如果它们选择这么做的话。

其次,你可能在想如何更容易地在容器中部署EJB,或者甚至根本不用部署,而是在独立环境中的容器外面进行测试。因为实体是具体的普通Java对象(POJO),你就可以像一直以来创建Java对象的方式那样,即使用new()来创建。

POJO应用程序

可 以通过实体管理器(EntityManager)访问大部分新的持久性API。实体管理器可以注入到会话bean里面,或者用Java命名和目录接口 (JNDI)进行查询。实体管理器代表事务的持久性上下文。一旦发现操作单元或者实体管理器管理的对象在事务结束后“很脏”,就会被写到外面的数据存储 区。

应用程序可以通过抽取含有操作单元/会话工件(artifact)的代码,让自己不受全面变化的影响。这 样一来,就可以通过可插入方式来获得所用的实际会话。定义会话、然后允许包围层把它与外界隔离开来,这类似EJB 3.0容器所采用的依赖注入范例(dependency injection paradigm)。应用程序中使用的所有资源应当采用这种模式。在EJB 3.0中,标准资源将由应用程序声明,随后在运行时被注入到bean里面。

采用标准特性

EJB 3.0的许多特性可以在TopLink存在已久的特性当中找到影子。只要使用这些特性,你就能够拥有EJB 3.0的功能,虽然API还没有完成。

查询就是这样一个方面,你现在可以开始使用EJB 3.0的特性。EJB 3.0查询可以从实体管理器获得,并且可以在上面执行。可以在你需要直接查询SLQ、并通过查询返回对象的少数情况下,创建本地SQL查询。

查 询语言往往是造成迁移问题的根源,因为不编写实质性或者穷举性的转换分析工具,就很难实现自动转换。EJB查询语言这种合理、有效的方法可对关系查询语言 进行抽象处理,将受益于新增的几项特性。现有的EJB查询语言仍可以适用,不过EJB查询语言方面的更多构件和功能将进一步改进查询语言。使用EJB查询 语言编写查询将是明智之举,因为查询语言不会出现重大改变,除了功能上有所添加外。

继承

EJB 2.1从来没有指定真正的、自然的继承。实际上,只有在厂商不设置障碍的情况下才有可能实现继承,不过仍很难定义及管理。EJB 3.0却不会这样。由于具体的Java对象能够彼此继承,也用不着定义约束继承范围的方法,所以你能够创建任意深度及广度的实体继承层次。

目 前可以通过TopLink Mapping Workbench或JDeveloper、映射Java对象的GUI工具以及用于映射对象的基于Java的API,获得同样的这种灵活性。现在你可以创 建域模型(domain model),以遵守适合你应用程序的继承策略,而不必等规范发布。

乐观锁定

TopLink 支持的乐观锁定(optimistic locking)模型现在将被采用到EJB 3.0模型里面。这种机制对应用程序非常有用,不仅仅是因为在读/写访问比通常为90:10的情况下,它可以大大提高性能;还因为它有助于获得现代系统所 需的那种可扩展架构。业界的众多应用程序使用这种主要的锁定范例,来获得Web应用程序所需的可扩展性。只要使用简单方法:为每个乐观锁定的对象采用数据 库列和对象版本字段,就很容易获得可移植性。

这种锁定带来了另外的好处:能够使用非连接模式的对象。只要把数据重新并入事务、然后通过乐观锁值验证已改动的对象是不是失效副本,就很容易支持在离线状态下改动数据和关系的功能。

对象-关系映射

想编写面向对象的Java程序,却把数据保存在关系数据库里面,这在以前是困扰应用程序开发的一个重大问题。

决 定把用于对象关系映射的标准化元数据和语义添加到EJB 3.0内,这向实现下列功能迈出了一大步:让应用程序能够灵活地把应用程序放在不同数据库上面运行,甚至可以使用不同的持久性框架来进行运行。对象-关系 映射标准的第一个阶段将包括如今人们用来映射域模型的几种最流行的映射方法,譬如数据转换和一对一及多对多关系等。随后还会添加“高级”映射方法。

 
展开阅读全文

没有更多推荐了,返回首页