7.1 EJB体系结构

7.1.1 EJB构件概述

1. EJB构件技术

EJB规范采用的主要构件技术包括:

  • 分布式对象技术:分布式对象技术提供给客户端「访问分布式对象的基本支持」。除了EJB之外,其它的分布式对象技术包括 Java RMICORBADCOM 等。所有的分布式对象技术,都会使用某个特定的远程方法调用协议,EJB中最常用的远程方法调用协议是 RMI/IIOP不论其采用什么具体的远程方法调用协议,现有分布式对象技术一般均采用以前提到的 Stub/Skeleton 结构,以支持客户端与分布式对象之间的交互
  • 服务端构件技术:服务端构件技术用于中间层应用服务器,支持分布式商业对象的开发。服务端构件技术以组件方式,提供系统的可重用性与可扩展性,随着实际商业系统的变化,可以重新组装、修改、甚至删除商业对象,而不必重新重写整个应用系统
  • Component Transaction Monitor, CTM 技术:简单地说,CTM 是一个应用服务器,它为分布式商业对象提供公共服务框架,CTM 公共服务框架支持大量的系统级服务,如事务 Transaction 管理等。

综上所述,EJB采用的构件技术为我们刻画了EJB构件的基本特征EJB构件是由公共服务框架自动管理的、分布式的服务端商业构件,其中 CTM 技术提供了公共服务框架的支持,分布式对象技术提供了分布式对象的支持,而服务端构件技术提供了服务端构件管理的基本支持

2. EJB构件的特点

作为Java EE应用中的核心构件,EJB构件除了具有一般软件构件的基本特征外,还具有以下主要特点:

  • 公共服务框架:EJB将「实现商业对象所使用的服务框架的任务」划分给了EJB应用服务器,服务框架支持大量的、由应用服务器提供的系统级服务。EJB应用服务器提供的服务框架,使得应用开发者可以关注于应用商业逻辑的实现,从而大大提高了开发效率,缩短了应用的开发周期
  • 平台独立性:平台独立性一方面得益于EJB沿袭了Java技术的平台无关性,另一方面,EJB/J2EE规范的开放性,使得「构成EJB应用的商业对象」可以移植到「任何符合EJB规范的应用服务器」上。
  • 封装特性:EJB规范提供对服务的封装特性 Wrap and Embrace封装特性使得EJB应用可以使用现有的基础性服务(如目录服务)——EJB通过定义一系列标准的服务API来封装现有的基础性服务,EJB构件通过这些标准的API来使用服务。如 JNDI 接口支持访问现有的命名目录服务(如 LDAP, COS ),通过使用 JNDI ,EJB应用服务器厂商可以将 LDAP 服务集成到其产品中,而不需去重新实现 LDAPEJB的封装特性,可以使应用服务器厂商节省IT投资,厂商可以使用现有的基础性服务,而不必去重新实现这些服务
  • 可定制性:EJB构件可以在不修改源代码的基础上进行定制化。EJB构件的定制是指,修改EJB构件的运行时配置、以满足特定用户的需求。EJB构件的定制主要通过布署描述符完成,比如可以利用布署描述符改变EJB构件的事务管理特性。除了EJB模块中标准的布署描述符 EJB-jar.xml 外,J2EE平台提供商通常还会提供特定的辅助布署描述符,以支持更强的可定制特性,如 Websphere 中的 ibm-ejb-ext.xmiWeblogic 中的 weblogic-ejb-jar.xml 等。
  • 协议无关性:指EJB构件支持客户端通过多种EJB访问EJB构件。EJB规范并没有强制约定只能通过 IIOP 协议来访问EJB构件,客户端可以通过其它通信协议,如 Weblogic 中支持的 t3 协议(一种基于HTTP的协议)来访问远程EJB构件。协议无关性使得EJB支持多种类型的客户端,不同类型的客户端使用不同的通信协议与EJB应用通信
  • 通用性:指EJB规范可方便支持不同规模的应用系统,即可以在任何时间增加客户系统、而不需修改核心的应用系统。通用性通常意味着系统资源的可伸缩性,系统资源在软件构件处理客户请求时会使用到(如处理器),随着系统规模的增大,构件可能要处理来自大量的客户请求,这里往往需要增加冗余的系统资源,如提供多个处理器(服务器),同时还需要一种合理的负载均衡机制、将客户端请求均匀地分发到不同的服务器上
    基于EJB容器的基本支持,Java EE平台(尤其是商用的平台)可以方便的支持不同规模的应用系统。如 Websphere 提供的克隆机制,可以支持管理员将某个J2EE应用复制多份、在多个服务器上运行,同时为多份服务提供自动的负载均衡机制。

3. 在EJB应用中集成遗产系统

遗产系统是现代企业信息流的主干,它体现了机构动作的业务逻辑,代表了特定领域的专业知识,是企业的财富,因此不能简单抛弃,通常需要在企业新开发的业务系统中实现遗产系统的集成

遗产系统通常是平台相关的,不能在网络环境中直接访问,并且遗产系统不能直接访问「存储在各种数据库管理系统中的数据」。因此一般说来,集成遗产系统的主要任务有两个,一是使遗产系统成为可以在网络中访问的平台无关系统,另一个任务就是支持遗产系统访问各种数据库

在EJB应用中,首先可以基于 JCA 实现在网络上访问遗产系统的目的,通过为遗产系统提供特定的适配器 Connector ,可以在EJB应用中通过 JCA 来访问遗产系统。除了利用 JCA 外,由于EJB构件可以通过 Java IDL 接口访问 CORBA 构件,因此,可以将遗产系统包装成 CORBA 对象、供EJB应用访问,从而达到可以在网络上访问遗产系统的目的。

4. EJB构件与 Java Bean 的比较

EJB与 Java Beans 都是基于Java语言的构件模型,开发应用时,可以选择EJB模型,也可以选择 Java Beans 模型。EJB与 Java Bean 的区别主要包括以下几点:

  1. 在大型的Java EE应用中,EJB构件通常用于服务端应用开发,而 Java Bean 构件通常用于客户端应用开发、或作为服务端EJB构件的补充。当然,也可以用 Java Bean 构件进行服务端应用的开发,但与EJB构件相比,Java Bean 不能使用Java EE平台提供的公共服务框架的支持,当应用需要使用关键的公共服务(如事务控制服务)时,使用普通的 Java Bean 构件显然不适合。
  2. EJB构件是可布署的,即EJB构件可以作为独立的软件单元、被布署到EJB应用服务器上,是应用构件 application components ;而 Java Bean 是开发构件,不能被部署为独立的单元。
  3. EJB构件是布署时可定制的,开发人员可以通过布署描述符,对EJB构件的运行时配置进行定制;而 Java Bean 构件的定制通常仅发生在开发阶段,开发人员只能利用开发工具创建并组装 Java Beans 构件,部署时不能对其进行定制。
  4. EJB构件是分布式对象,可以被客户应用、或者其它EJB构件进行远程访问;而普通的 Java Bean 构件只能在其构成的应用中使用,不能提供远程访问的能力。
  5. EJB构件是服务端构件,运行在服务端,没有人机交互界面,对终端用户不可见;而部分 Java Beans 构件对终端用户可见,如GUI应用中使用的按钮构件等。

综上所述,从对比的角度看,可以认为EJB是Java提供的服务端构件模型,而 Java Bean 则是Java提供的客户端构件模型,具体开发时可根据应用的具体需求,结合EJB和 Java Bean 的特点,灵活的选择EJB或者 Java Bean

7.1.2 EJB体系结构中的构件

广义地讲,EJB体系结构中涉及以下 6 6 6 类软件构件:

  • Enterprise Java Bean(简称 Enterprise Bean
  • Home 接口
  • Remote 接口
  • EJB容器
  • EJB容器
  • EJB客户端

1. Enterprise Bean

所谓 Enterprise Bean ,就是开发者实现的核心构件EJB,是 「EJB客户端所调用的操作」的真正实现者,可以被部署到EJB应用服务器上,用来组装大型的EJB应用。开发人员可以开发的EJB构件包括会话构件 Session Bean实体构件 Entity Bean消息驱动构件 Message Driven Bean

  • Session bean 存在于客户应用与应用服务器交互的时间段内,是用来和客户端做交互的。和实体构件相比,会话构件中的数据不保存在数据库中。会话构件又可分为两类,有状态的会话构件 Stateful Session Bean无状态的会话构件 Stateless Session Bean 。无状态的 Session bean 在方法调用中间不维护任何状态,一个无状态的 Session bean 实例可以同时处理多个客户应用的请求,典型地如:网上证券系统中提供股票信息查询功能的会话构件。而有状态的 Session bean 要跨方法调用保存会话状态,一个有状态的 Session Bean 实例同时只处理一个客户应用的请求,典型地如:网上购物系统中提供购物车功能的会话构件。
  • Entity Bean 代表数据库中的记录,在EJB中是用来封装数据库操作的。与会话构件相比,逻辑上可以认为,实体构件在数据库中的数据存在期间都会存在。同样与数据库中的数据类似,实体构件可以被多个客户应用共享访问
  • Message Driven Bean 主要用来处理异步消息,因此通常在异步编程模式下使用。消息驱动构件实现的方法不是被客户端直接调用的,而是当有异步消息发送到某个 Message Driven Bean,容器会调用它的的回调方法——OnMessage ,构件实现者在 OnMessage 方法中实现对异步消息的处理。

2. Home 接口

Home 接口 Home Interface 包含EJB生命周期管理的相关方法,客户程序使用 Home 接口,创建或删除EJB的实例。应注意从严格意义上讲,接口不能称作构件,因为接口中仅包含有方法的声明,而没有实现。这里将 Home 接口称为构件,是因为在EJB体系结构中,每个 Home 接口都依赖于一个类 bean 来提供 Home 接口中约定的功能,只不过这个特殊的类不是程序员编写的,而是由容器自动生成的,开发人员只需要编写 Home 接口;同样对于EJB的客户端程序来讲,也只能看到 Home 接口,看不到这个实现类。

3. Remote 接口

Remote 接口中包含EJB实现的商业方法的声明,它实际上约定了EJB所提供的服务。这里说 Remote 接口是构件,与 Home 接口是类似的,容器也会自动生成一个 bean 来实现这个接口中的方法,但应注意:容器生成的并不是真正的服务实现,真正的操作是由EJB构件实现的。在EJB中,客户程序只能通过 Remote 接口来间接地访问EJB实现的商业方法,不能直接进行调用。

基于以上三种构件,我们可以得到图7-1所示的、EJB体系结构中的 Stub/Skeleton 结构
图7-1 EJB体系结构中的 Stub/Skeleton 结构
在客户端,客户程序通过两个接口来访问远程的EJB构件,通过 Home 接口调用EJB生命周期管理相关的操作,通过 Remote 接口调用EJB构件提供的业务逻辑操作,远程调用均通过 Stub/Skeleton 结构完成。应注意,基于 Stub/Skeleton 结构与客户端直接交互的,并不是EJB构件(图中的 Enterprise Bean ),而是容器自动生成的——实现 Home 接口中操作的类 Home Bean 和实现 Remote 接口中操作的类 Remote Bean ,由于这两个类是容器自动生成的,因此从逻辑上看,客户程序与EJB构件之间的每次交互均由容器充当了中介

之所以采用这种看起来比较复杂的结构,其中一个重要的原因是基于这种结构,容器可以利用中介的身份为EJB构件提供各种公共服务,如客户程序访问EJB构件时,容器可以根据布署描述符中的配置信息,检查客户端是否满足「管理员定义的安全控制规则」(如该客户是否有权限访问它正在调用的操作等),如果不满足则不会允许其访问EJB。而实现这种控制,并不需要开发人员编写任何Java代码,只需在布署描述符中进行必要的配置就可以完成

EJB 2.0 之后引入了一对与 Home 接口、Remote 接口完成类似功能的本地接口——LocalHome 接口和 Local 接口。Local 接口完成与 Remote 接口类似的功能,包含EJB实现的商业方法的声明,LocalHome 接口完成与 Home 接口类似的功能,包含EJB生命周期管理的相关的方法。Home 接口和 Remote 相比,本地接口的不同之处在于:客户应用通过本地接口发起的调用是进程内的本地调用,因此比远程接口调用有更高的效率;使用本地接口要求客户端和EJB在同一个进程(虚拟机)内,如一个 Session Bean 需要访问同一容器内的某个 Entity Bean 时,可以使用 Entity Bean 的本地接口发起调用,以获得更高的执行效率。

每个EJB构件都有一对「对应的 Home 接口与 Remote 接口」和/或一对「对应的 LocalHome 接口与 Local 接口」,从这种意义上讲,我们通常认为一个完整的EJB构件包含 Enterprise Bean 类、Home 接口与 Remote 接口三部分。EJB规范3.0对EJB对应的接口进行了一次简化,相关信息请参阅第9章第4节。

4. EJB容器

EJB容器为EJB构件提供运行环境,并管理运行于其中的EJB,理论上讲,一个EJB容器可以包含任何数量的EJB,但是由于实际资源的限制,实际的J2EE平台的容器,往往有一个能够包含EJB构件的上限。EJB容器为EJB的执行提供系统级的服务,如自动将EJB相关的 Home 接口注册到一个目录服务中,自动注册服务支持客户应用查找定位EJB的实例。

5. EJB服务器

EJB服务器是遵循EJB定义的构件模型的 CTM 实现,一个EJB服务器可以包含一个或多个EJB容器,EJB服务器为EJB容器的运行提供公共服务框架。公共服务框架支持系统级服务,如 JNDI 服务。从使用服务的角度来看,开发人员可以不区分EJB容器与EJB服务器,可以认为EJB容器和EJB服务器提供的服务都是由容器提供的

6. EJB客户端

EJB客户端泛指「调用EJB构件提供的业务操作的软件实体」,EJB构件的客户端可以有多种形式。如EJB的客户端可以是独立的Java程序,也可以是运行在Web容器中的 ServletJSP 构件,ServletJSP 形式的EJB客户端响应Web客户的请求;EJB的客户端还可以是其它的EJB,例如 Session Bean 经常作为 Entity Bean 的客户端来访问持久数据。


7.2 EJB设计原则

通过上节的讨论知道,每个EJB构件通常都有对应的一个 Home 接口与一个 Remote接口,这三种构件在EJB应用中通常作为一个整体出现。在开发时,EJB构件的实现类与对应的两个接口之间,必须符合EJB规范所约定的对应关系与相关的设计原则。

7.2.1 接口设计原则

1. Remote 接口设计原则

Remote 接口约定了EJB构件提供的业务逻辑操作,编写 Remote 接口时应遵循以下原则或约束:

  • 继承性约束:每个 Remote 接口必须继承 EJBObject 接口,该接口位于 javax.EJB 包,其中包含用于管理「实现 Remote 接口的EJB对象」的方法,这些方法是每一个EJB对象都需要的。该接口在EJB规范中的作用,相当于 CORBA 规范中 Object(见程序 3-3)接口的作用,用于约定特定体系结构下构件必须满足的特定约束
  • 方法对应规则Remote 接口中出现的每一个方法的声明,都必须在相应的 Enterprise Bean 类中有一个对应方法的实现,因为 Remote 接口是服务的约定,而 Enterprise Bean 类是服务的真正提供者,所以这一条约束是必然的。其中,每个方法的参数和返回值必须完全相同,抛出的异常必须匹配。应注意这里是匹配,而不是相同,匹配的含义是指接口中「方法抛出异常的集合」必须包含 Bean 类中「对应方法抛出异常的集合」。即接口方法中出现的异常,Bean 类中可以出现,也可以不出现,但是不允许 Bean 类中方法抛出「接口对应方法中没有声明的异常」。
    这一点与 CORBA 或者 Java RMI 不同,方法的声明和方法的实现在接口上可以不完全匹配,因为EJB的客户端通过 Remote 接口方法连接的不直接是 Enterprise Bean 类、而是容器生成的一个类 Remote Bean ,每次客户端请求都由容器来充当中介。需要注意的是,在Java程序设计语言的层次上并没有提供机制来保证这一点,也就是说在定义 Enterprise Bean 类时,并不声明该类实现(implementsRemote 接口。即使编写的 Enterprise Bean 类与对应的 Remote 接口不符合该规则的约定,也可能可以单独利用 javac 编译通过,但是Enterprise Bean 类、Home 接口以及 Remote 接口打包成完整的EJB构件时,会进行相应的检查
  • RMI 约束Remote 接口中定义的方法应该遵循 Java RMI 的约束。因为EJB对象是分布式对象,所以 Remote 接口中约定的方法必须是可远程访问的,为了支持远程访问,定义 Remote 接口时必须遵循 Java RMI 标准。具体的规则包括:
    • Remote 接口中的方法必须抛出RemoteException 异常,该异常报告网络通信错误;
    • 方法定义中的参数与返回值必须是合法的 Java RMI 类型的参数/返回值,因为只有合法的 Java RMI 类型的参数/返回值才可以在网络中传递,如可串行化对象(实现了 java.io.Serializable 接口)等。

2. Home 接口设计原则

Home 接口中包含EJB构件生命周期管理相关的方法,支持客户应用创建、删除或定位Enterprise Bean 的实例。编写 Home 接口时应遵循以下原则或约束:

  • 继承性约束:每个 Home 接口必须继承 EJBHome 接口,EJBHome 接口位于 javax.ejb 包,其中包含了 Enterprise Bean 生命周期管理的方法,Home 接口类似于 CORBA 体系结构中的工厂概念,开发人员可以在接口的基础上,补充自定义的方法。
  • 方法对应规则:EJB规范要求——Home 接口中的每个 create 方法都必须在相应的 Enterprise Bean 类中有一个对应的 EJBCreate 方法,函数接口必须相同,返回值要符合相应的匹配规则,抛出的异常必须匹配,匹配的含义与 Remote 接口方法对应规则中的匹配含义相同,即 create 方法抛出的异常的集合包含 Bean 类中对应 EJBCreate 方法抛出异常的集合
    EJB构件的 create 方法支持客户端获取可用的EJB实例的引用,客户程序调用 create 方法时,EJB容器会调用相应的 EJBCreate 方法完成 Enterprise Bean 实例的初始化。同样,在Java程序设计语言的层次上并没有提供机制来保证这一点,将 Enterprise Bean 类、Home 接口以及 Remote 接口打包成完整的EJB构件时,会进行相应的检查。
  • RMI 约束Home 接口中约定的操作也是被远程调用的,因此 Home 接口中声明的每个操作也必须符合与 Remote 接口的操作相同的 RMI 约束,即必须抛出 RemoteException ,参数/返回值必须是合法的 Java RMI 类型的参数/返回值。除此之外EJB规范还要求,Home 接口中的每个 create 方法必须抛出 CreateException 异常,该异常用于报告EJB实例的初始化错误。

7.2.2 类设计原则

1. Enterprise Bean 类设计原则

Enterprise Bean 类包含客户端调用的业务逻辑操作的真正实现,是程序员编写的EJB构件的核心代码,编写 Enterprise Bean 类时应遵循以下原则:

  • 接口约束Enterprise bean 类必须实现 EnterpriseBean 接口,EnterpriseBean 接口是java.io.Serializable 接口的子接口。EnterpriseBean 接口中定义了 Enterprise Bean 生命周期管理的方法,实现该接口是 Enterprise Bean 与普通 Java Bean 的重要区别。开发人员应该了解不同类型的EJB构件的生命周期特性,需要了解 EnterpriseBean 接口中相关生命周期管理方法的调用时机
  • 可见性约束Enterprise bean 类必须定义为 public 类。由于EJB客户程序不能直接访问 Enterprise Bean 类实现的方法,客户程序的请求被EJB容器生成的EJB对象接收到,由EJB对象根据客户请求、调用 Enterprise Bean 实例的对应方法Enterprise Bean 类定义成 public 类、以允许其它的类(如容器生成的EJB对象)访问 Enterprise Bean 类中定义的方法。
  • 商业方法约束Enterprise bean 类必须实现 Remote 接口中定义的业务逻辑操作。具体的对应规则请参照7.2.1中的 Remote 接口设计原则。
  • 生命周期管理方法约束Enterprise Bean 类必须实现 Home 接口中定义的 create 方法对应的 ejbCreate 方法。ejbCreate 方法完成 Enterprise Bean 实例的初始化,客户端调用 Home 接口中定义的 create 方法时,通常会导致EJB容器生成的 EJB Home 对象调用 Enterprise Bean 实例的对应 ejbCreate 方法,客户程序在调用 create 方法时传递的参数,被用来调用ejbCreate 方法。

2. 主键 Primary Key 类设计原则

在EJB规范中,Entity Bean 代表数据库中的记录。与「数据库中的记录由主键唯一标识」相对应,Entity Bean 实例由主键类唯一标识。主键类对应数据库中的主键字段,相当于提供了一个指向数据库中记录的“指针”。编写主键类时应遵循以下原则:

  • 可见性约束:主键类必须定义为 public 类。定义成 public 类,以使得 Primary Key 所标识的 Entity Bean 总可以访问数据库表中的字段。
  • RMI 约束:因为 Primary Key 类也是要供远程调用的,所以定义时也要遵循类似的 RMI 约束。

思考与练习

7-1 在EJB体系结构中,Home 接口与 Remote 接口的主要作用是什么?为什么还要引入 LocalHomeLocal 接口,这对接口使用时有哪些限制?
7-2 EJB构件与普通的 Java Bean 有哪些主要区别?他们分别适合于什么场合的开发?
7-3 EJB体系结构中基于 Stub/Skeleton 结构与客户端交互的直接构件不是EJB,而是容器自动生成的对象,采用这样的结构有什么好处?

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐