您的位置:首页 > 其它

企业Beans(EJB)简介

2006-12-27 13:07 351 查看
ejb入门

一.Enterprise Beans

在EJB(Enterprise Java Beans)中定义了两种不同类别的Enterprise Bean :

*会话 Bean (Session Bean)

*实体Bean (Entity Bean)

1. 会话 Bean (Session Bean)

会话 Bean 是调用它的客户端代码要完成的工作。当客户端与服务器建立联系,那么一个会话 Bean 就建立起来了。根据会话 Bean 的状态不同有分为:

状态会话 Bean (Stateful Session Bean)

无状态会话 Bean (Stateless Session Bean)

1.1 状态会话 Bean (Stateful Session Bean)

当客户机和服务器建立连接之后,状态会话 Bean (Stateful Session Bean) 将一直在客户机和服务器之间保持着用户的某个状态。例如:用户使用银行的ATM时,经过验证之后,用户可以连续执行多次操作,在这个过程当中,用户的合法状态将一直被保留,直到她将信用卡取出,结束这次操作。这时,状态会话 Bean (Stateful Session Bean) 也就被销毁。

1.2无状态会话 Bean (Stateless Session Bean)

当客户机和服务器建立连接之后,无状态会话 Bean (Stateless Session Bean)处理单一的用户请求或商务过程。无状态会话 Bean (Stateless Session Bean)不需要从以前的请求中提取任何状态。例如,用户的用户密码确认。用户输入密码后,发送请求。组件返回真或假来确认用户,一旦过程完成,无状态会话 Bean (Stateless Session Bean) 也宣告结束。

2. 实体Bean (Entity Bean)

实体Bean (Entity Bean)只是数据模型,它不包括商务逻辑。实体Bean (Entity Bean)可以将关系/对象数据库的数据映射到内存中供其它组件使用。实体Bean (Entity Bean)是一直存在的,而且具有很高的容错性能。实体Bean (Entity Bean)能供允许多用户同时访问。

二. 会话 Bean (Session Bean)

Ejb的执行过程是被放在一个EJB容器中进行的,所以客户端不会直接调用我们写好的Enterprise Bean ,而是调用EJB容器生成的一个EJBObject (EJB对象)来实现。那么,我们在编写服务器端的Enterprise Bean 时,就要考虑这点。既然客户端不能直接访问,就由EJBObject来代劳,所以在编写服务器端时,就要编写服务器端的一个接口(Remote)用来与客户机联系,实力化EJBObject。要生成EJBObject 就要调有Home 接口,来建立这个实力。



图一

以下是会话 Bean 的代码分析:

A.Enterprise Bean 类:sailorsy.class

1.setSessionContext(SessionContext ctx)方法

它是EJB容器和Enterprise Bean互相作用的关口。

import java.rmi.*;

import javax.ejb.*;

public class sailorsy implements SessionBean{

private SessionContext ctx=null;

public voic setSessionContext(SessionContext ctx){

this.ctx=ctx;

}//setSessionContext

}//class sailorsy

2.ejbCreate(…)方法

它可以初始化Enterprise Bean ,可以定义不同的ejbCreate(…)方法,每个方法所带的参数不同。但是,必许要存在至少一种。

import java.rmi.*;

import javax.ejb.*;

public class sailorsy implements SessionBean{

private SessionContext ctx=null;

public voic setSessionContext(SessionContext ctx){

this.ctx=ctx;

}//setSessionContext

public void ejbCreate() {

}//ejbCreate

}//class sailorsy

3.ejbPassivate()方法

如果初始化的Enterprise Bean 过多,EJB容器将其中的一些挂起(passivate),释放他们所占用的空间。

import java.rmi.*;

import javax.ejb.*;

public class sailorsy implements SessionBean{

private SessionContext ctx=null;

public voic setSessionContext(SessionContext ctx){

this.ctx=ctx;

}//setSessionContext

public void ejbCreate() {

}//ejbCreate

public void ejbPassivate() {

}//ejbPassivate

}//class sailorsy

4.ejbActivate()方法

和ejbPassivate正好相反,它将被挂起的Bean从新调回。

import java.rmi.*;

import javax.ejb.*;

public class sailorsy implements SessionBean{

private SessionContext ctx=null;

public voic setSessionContext(SessionContext ctx){

this.ctx=ctx;

}//setSessionContext

public void ejbCreate() {

}//ejbCreate

public void ejbPassivate() {

}//ejbPassivate

public void ejbActivate() {

}//ejbActivate

}//class sailorsy

5.ejbRemove()方法

它可以清除EJB容器中的Bean。

import java.rmi.*;

import javax.ejb.*;

public class sailorsy implements SessionBean{

private SessionContext ctx=null;

public voic setSessionContext(SessionContext ctx){

this.ctx=ctx;

}//setSessionContext

public void ejbCreate() {

}//ejbCreate

public void ejbPassivate() {

}//ejbPassivate

public void ejbActivate() {

}//ejbActivate

public void ejbRemove() {

}//ejbRemove

public String showname(){

return "Hello,my name is sailorsy";

}//自己的商务方法

}//class sailorsy

以上这些是EJB必需的回调方法,我们可以在里面加入自己的方法,加入自己的商务逻辑。

B.Home 接口: sailorsyHome

import java.rmi.*;

import javax.ejb.*;

public interface sailorsyHome extends EJBHome {

public sailorsyRemote create() throws RemoteException, CreateException;

}

Remote接口:sailorsyRemote

import java.rmi.*;

import javax.ejb.*;

public interface sailorsyRemote extends EJBObject {

public java.lang.String showname() throws RemoteException;

}

三.调用会话 Bean:sailorsyTestClient1

import javax.naming.*;

import javax.ejb.*;

import javax.rmi.PortableRemoteObject;

import java.rmi.*;

public class sailorsyTestClient1 {

private sailorsyHome sailorsyHomeObject = null;

//Construct the EJB test client

public sailorsyTestClient1() {

try {

//以下是客户端使用JNDI定位Home对象。

Context ctx = new InitialContext();

//look up jndi name

Object ref = ctx.lookup("sailorsy");

//cast to Home interface

sailorsyHomeObject = (sailorsyHome) PortableRemoteObject.narrow(ref, sailorsyHome.class);

}

catch(Exception e) {

e.printStackTrace();

}

}

//----------------------------------------------------------------------------

// Utility Methods

//----------------------------------------------------------------------------

public sailorsyHome getHome() {

return sailorsyHomeObject;

}

//Main method

public static void main(String[] args) throws Exception{

sailorsyTestClient1 client = new sailorsyTestClient1();

sailorsyRemote sr=client.getHome() .create() ;



Home 接口

String s=sr.showname() ;


System.out.print(s);

// Use the getHome() method of the client object to call Home interface

// methods that will return a Remote interface reference.Then

// use that Remote interface reference to access the EJB.

}

}

以上的EJB在win2000+jbuilder5/jbuilder6+BAS4.5经过测试。



图二

以下是整个EJB的执行过程:

客户端通过JNDI检索Home对象的引用;

JNDI返回Home对象的引用;

请求创建一个新的EJB对象;

创建EJB对象;

返回EJB对象;

调用商务方法;

调用Enterprise Bean.

1.2 测试平台安装

  J2EE应用开发、测试或运行需要应用服务器的支持。本书中以Windows版的WebLogic Server 6.0sp2测试版为J2EE应用程序的运行平台,关于WebLogic Server 6.0sp2测试版的免费获取和产品注册请参阅Bea中国公司的网站www.bea.com.cn。
   本书中所指的运行和开发环境的操作系统为Window 2000 Professional简体中文版。
   WebLogic Server 6.0sp2测试版的安装程序做成了一个独立的压缩包运行程序——weblogic600sp2_win.exe。这个文件大概有60M,需要硬盘110M左右的空间。在Windows操作系统下运行这个程序,按照每个安装界面的提示进行安装。具体如下:
   (1)在资源管理器中,双击weblogic600sp2_win.exe,运行Weblogic Server 6.0sp2测试版的安装程序。
   首先出现欢迎界面。如图1-2所示(略)。
   (2)点击Next按钮,出现关于授权协议的条款,选中“Yes”,如图1-3所示。
   (3)点击Next按钮,选择安装集。此时选中Server With Example字体左边的按钮,如图1-4所示。
   (4)点击Next按钮,选择BEA Home目录和安装目录,可以采用缺省目录C:/bea和C:/bea/wlserver6.0,如图1-5,图1-6所示。
   注意:请一定要采取这个安装目录,这样就和本书的讲解一致。
   (5)点击Next按钮,进行缺省的服务器配置。设定域名、服务器名、端口号和SSL端口号。这里采用缺省值如图1-7所示。
   (6)点击Next按钮,确定是否把WebLogic作为Windows的服务。这里采用缺省的“No”,不作为Windows服务,如图1-8所示。
   (7)点击Next按钮,创建系统级密码,注意密码长度至少8个字符,如图1-9所示。
   (8)点击Next按钮,进入安装过程,出现安装进度条。如图1-10所示。
   (9)进度完成后,出现如图所示的界面,点击Done按钮完成安装。

1.2.1 管理服务器

  WebLogic Server 6.0采用一套独特的管理方法。WebLogic Server可以定义几个同等地位的域(Domain),但对于一个WebLogic Server主机来说,同时只能启动一个域。在域中定义几个服务器(Server),这些服务器中必须有一个称作系统管理服务器(Administration Server),其它的服务器称作被管理的服务器(Managed Servers)。在一个域中,只能有而且必须要有一个系统管理服务器,但可以有多个被管理的服务器。
   在WebLogic Server 6.0版本中,每个域对应一个独立的子目录,这些目录位于config子目录下,如图1-12所示。
   缺省安装的情况下,WebLogic Server 6.0有三个域,分别是mydomain,examples和petstore。它们对应config目录下的与域名相同的三个子目录名。
   WebLogic Server 6.0缺省的三个域各有各的用途,examples域集中给出了各种实例程序,用户可以直接进行运行测试;petstore是个具体的电子商务应用实例,用户也可以进行运行测试;mydomain是供用户建立用户开发的应用的域,缺省情况下,它不包含任何应用。
   在域子目录下是应用子目录和服务器运行等脚本文件。如图1-13所示。

1.2.2 域服务器和管理控制台

  安装完WebLogic Server 6.0sp2后,可以通过开始菜单来运行域服务器。因为有三个缺省的域,所以开始菜单中有三个运行域的快捷菜单项。
   如图1-14所示,启动域名为mydomain的服务器。
   也可以直接运行C:/bea/wlserver6.0/config/mydomain目录下命令脚本startWebLogic.cmd来启动域服务器。
   缺省服务器启动窗口如图1-15所示。
   输入密码,服务器正确启动后,如图1-16所示。
   现在可以进行测试了。打开浏览器,在地址栏中输入http://localhost:7001/。如图1-17所示即是缺省域的缺省页面。
   WebLogic Server 6.0的系统管理采用Web应用的方式,系统管理是通过浏览器进行的。启动系统管理控制台程序,可以采用图1-18所示的操作。
   启动系统管理控制台程序要求输入系统密码。如图1-19所示。用户名一定是system,密码就是在安装WebLogic Server的过程中设定的系统密码。
   密码正确,就进入系统管理程序界面,如图1-20所示。
   WebLogic Server 6.0的系统管理程序的启动也可以直接在浏览器的地址栏中输入http://localhost:7001/console/,然后回车。
   WebLogic Server域服务器的启动和系统管理的启动先介绍这些。examples域和petstore域的启动分别如图1-21和图1-22所示。关于系统管理控制台程序的详细操作会在下面的章节中详细描述。
   注意:同时只能启动一个域服务器。

1.2.3 操作域服务器

  操作域服务器通过系统管理程序可以挂起、恢复和停止域服务器。下面演示了怎样通过console页面程序进行这些操作:
   (1)打开服务器管理控制台程序console。即浏览http://127.0.0.1:7001/console。
   (2)点击左侧树状视图:“mydomain”->“Servers”->“myserver”。
   (3)点击右侧视图的“Monitoring”页,如图1-23所示。
   (4)点击视图中的“Shutdown this server...”,就可以关闭服务器,服务器挂起和恢复分别点击“Suspend this server...”和“Resume this server...”。

企业Bean(Enterprise JavaBean,EJB)在J2EE应用中处于核心地位。EJB程序的开发是实现J2EE倡导的分布式企业级组件应用的重要组成部分。按照EJB模式开发的应用程序在大型电子商务(e-Commerce)、企业应用集成(Enterprise Application Integration,EAI)等方面表现出无可比拟的优势,目前已被多家大型IT厂商支持,成为最广泛的分布式组件应用规范。
   EJB应用的基本模式是容器/组件应用模式。容器对应EJB服务器或应用服务器,如Weblogic Server等;组件就是EJB程序。对开发者来说,只关注与业务逻辑相关的组件程序,而不必管分布式应用带来的并发、大量事务完整性等问题,从而使开发分布应用不必再跨越很高的门槛,使其简单可行。
   无论哪种类型的EJB应用程序,它们的开发步骤都是类似的。掌握了一个简单EJB的开发过程,就可以进一步开发更加复杂的应用。本章假设读者从来没有开发过EJB,也不具备EJB的任何知识而准备以最简捷的方式开发出一个简单的EJB应用。
   在本章开发一个简单的EJB之前,假设读者已安装了WebLogic Server 6.0sp2。关于WebLogic Server 6.0的安装和简单介绍,请看第1章。
   本章的简单实例的名字叫HelloWorldEJB。它只有一个简单的方法sayHello,用来模拟业务逻辑。客户端调用这个方法,获取字符串并显示出来。更复杂情形的实例会在后面几章给出。好,准备你的EJB之旅吧!

2.1 准备工作
 其实并没有太多的准备工作要做,但为了保持整洁、有序、清晰的工作环境,做一些简单的准备还是很有必要的。

2.1.1 确定工作目录

  为了本书叙述方便,这里为读者指定了一个存放源程序的目录,可以把它叫做工作目录,工作目录确定为c:/work,读者当然可以选用其它的目录(如有些人认为把C盘作为工作目录不好,而把工作目录建在D盘),如果这样,就要做好与作者叙述的映射工作,以免出现问题。

2.1.2 编写环境变量脚本

  开发Java程序过程中,环境变量是程序员要管理的一件事。这些环境变量包括系统环境变量,如path、classpath和用户定义的环境变量。当环境变量较少时,可以直接把它们设置成系统级的,但如果环境变量较多,就不适宜把它们都设成系统级的,这时用一个脚本程序来设置这些环境变量是个很的选择。
   在本书中用到的所有实例的环境变量,包括系统级的和用户级的,都用一个环境变量脚本程序来设置。在具体使用的时候,无论是编译,还是执行,只要先运行一下这个脚本命令即可。
   这个脚本命令名为setEnv.cmd,放在C:/work目录下,可以在命令窗口(有人习惯称为DOS窗口或控制台窗口)的任何路径下运行,运行命令如下:
   c:/work/setEnv.cmd
   读者可以从配套光盘的/work路径下找到setEnv.cmd,把它拷贝到C:/work。也可以用文本编辑器编写,以setEnv.cmd的名字保存到路径C:/work下。其代码如下:

rem 设置系统信息
set J***A_HOME=C:/bea/jdk130
set WL_HOME=c:/bea/wlserver6.0
set DOMAIN_NAME=mydomain

rem 以下不用修改
set path=%path%;%J***A_HOME%/bin
set classpath=.;%classpath%;%WL_HOME%/lib/weblogic.jar
set EX_WEBAPP_CLASSES=%WL_HOME%/config/%DOMAIN_NAME%/applications/DefaultWebApp_myserver/WEB-INF/classes
set CLIENT_CLASSES=%WL_HOME%/config/%DOMAIN_NAME%/clientclasses
set SERVER_CLASSES=%WL_HOME%/config/%DOMAIN_NAME%/serverclasses
set classpath=%classpath%;%CLIENT_CLASSES%;%SERVER_CLASSES%
set Classpath=%classpath%;%J***A_HOME%/lib/tools.jar

2.1.3 文本编辑器

  为编写EJB程序代码和部署代码,需要一个文本编辑器。可以使用Windows自带的“记事本(NotePad)”编辑器。但建议选择功能强一些的文本编辑器,如UltraEdit,EditPlus等。
   准备工作先做到这里,下面就进行第一个EJB的开发了。

上一章通过开发一个简单的企业Bean,对EJB有了一个初步的认识,本章进一步讨论EJB技术体系的主要几个方面,阐述诸如EJB组件、EJB角色、EJB类型、持久性、EJB合约等基本概念。使读者对EJB技术有更加深入的了解。
   企业Bean(Enterprise JavaBeans,EJB)规范由SUN公司和其它伙伴公司制订,1.0版本在1998年3月发布。1.1版本在1999年12月发布。最近的版本是2.0,于2000年10月发布。可以从网址http://java.sun.com/products/ejb/docs.html下载该规范。
   本章以Enterprise JavaBeans(EJB)规范2.0版为主要参考,从多个侧面介绍EJB的基本概念。

2.3 命令脚本文件
  上面在EJB的开发过程中使用命令行的方式,我们可以把有所有这些命令行集中起来做成一个命令脚本文件build.cmd,使用它可以一次性执行这些命令,而不必一个命令一个命令的执行。该文件存放在C:/work/hello目录下,其内容为:

md build
javac -d build Hello.java HelloHome.java HelloBean.java
md build/Meta-inf
copy *.xml build/Meta-inf
md build/images
copy *.gif build/images
cd build
jar cv0f std_myfirstejb_hello.jar META-INF *.class images
cd ..
java weblogic.ejbc -compiler javac build/std_myfirstejb_hello.jar build/myfirstejb_hello.jar

  这样,在程序代码编辑好的情况下,只运行这个命令脚本程序,就可以完成EJB开发。

2.4 EJB部署
  EJB的部署有很多种方法,最简单的一种就是把C:/work/hello/build目录下的myfirstejb_hello.jar文件拷贝到服务器的applications目录下,即C:/bea/wlserver6.0/config/mydomain/applications目录下。如果服务器已经启动,则在拷贝的时候不需要重新启动服务器。服务器会自动检测到新拷贝的EJB。

2.5 EJB运行测试
  编写一个简单的客户端程序HelloClient来测试已部署的ejb。

2.5.1 客户端测试程序代码编写

  在文本编辑器中编辑HelloClient.java文件,并保存在C:/work/hello目录下,其代码如下:

//本类需要引入的类或接口
import java.rmi.RemoteException;
import java.util.Properties;
import javax.ejb.CreateException;
import javax.ejb.RemoveException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

/**
* 这是EJB的客户端测试程序
*/

public class HelloClient {

private static final String JNDI_NAME = "HelloHome";

private String url;
private HelloHome home;

public HelloClient(String url)
throws NamingException
{

this.url = url;

home = lookupHome();
}

void test()
throws RemoteException,CreateException
{
Hello hello = (Hello) PortableRemoteObject.narrow(home.create(), Hello.class);
System.out.println("I am in client. "+hello.sayHello());
}

/**
* 运行这个实例:
* java HelloClient t3://localhost:7001
*/
public static void main(String[] args) throws Exception {

System.out.println("/n客户端程序测试开始.../n");

String url = "t3://localhost:7001";

// 解析命令行
if (args.length != 1) {
System.out.println("用法: java HelloClient t3://hostname:port");
return;
} else {
url = args[0];
}
HelloClient client = null;
try {
client = new HelloClient(url);
} catch (NamingException ne) {
System.exit(1);
}
try {
client.test();
} catch (Exception e) {
System.exit(1);
}
System.out.println("/n客户端程序测试结束.../n");
}

/**
* 查找 EJB 的主接口
*/
private HelloHome lookupHome()
throws NamingException
{
Context ctx = getInitialContext();
Object home = ctx.lookup(JNDI_NAME);
return (HelloHome) PortableRemoteObject.narrow(home, HelloHome.class);
}

/**
* 使用属性对象获取上下文
*/
private Context getInitialContext() throws NamingException {
Properties h = new Properties();
h.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL, url);
return new InitialContext(h);
}
}

2.5.2 客户端测试程序代码编译

  请执行以下操作:
   (1)在hello目录下创建client子上当放置客户端测试程序,创建client目录,拷贝文件。执行:
    c:/work/hello>md client
    c:/work/hello>copy HelloClient.java client
   注意:因为HelloClient类需引入Hello和HelloHome接口,因此要从build目录下拷贝Hello.class和HelloHome.class文件到client目录。

   c:/work/hello>copy build/Hello.class client
    c:/work/hello>copy build/HelloHome.class client
   (2)进入client目录
    c:/work/hello>cd client
   (3)编译:
    c:/work/hello/client>java HelloClient.java
   至此客户端测试程序代码编译完毕。
   也可以把这些命令做成一个命令脚本文件build_client.cmd,保存在C:/work/hello目录,其代码如下:

md client
copy HelloClient.java client
copy build/Hello.class client
copy build/HelloHome.class client
cd client
javac HelloClient.java

2.5.3 运行测试

  运行测试程序,执行如下操作:
   (1)启动Weblogic Server 6.0的缺省服务器,Weblogic Server的启动是个简单的过程,可以通过开始菜单和直接运行启动命令脚本两种方式,详细情况请参见第1章。
   (2)运行测试程序。在命窗口中进入C:/work/hello/client目录,执行:
    c:/work/hello/client>java HelloClient t3://127.0.0.1:7001
   (3)观察运行结果:应该如下:
   客户端程序测试开始...
   I am in client. Hello World
   客户端程序测试结束...
而在Weblogic Server的命令行窗口中打印如下信息:
   I am in an EJB of Server .Hello World
并且每运行一次,这行信息就打印一次。

2.6 常见故障及解决方法
  (1)在命令行窗口中运行javac命令出现:
   "java"不是内部或外部命令,也不是可运行的程序或批处理文件。
   原因:javac.exe命令文件所有的路径没有包含在环境变量Path中。
   (2)执行javac -d build Hello.java HelloHome.java HelloBean.java 命令时出现编译错误,其中有一条错误是:
   cannot resolve symbol
   symbol : class EJBHome
   location : package ejb
   impor javax.ebj.EJBHome;
   原因:javac.exe命令文件甩在的路径没有被包含在环境变量Path中。
   解决方法:运行环境变量脚本命令setEnv.cmd。
   (3)运行客户端测试程序时,即运行:
   java HelloClient t3://127.0.0.1:7001时,出现:
    Exception in thread "main" java.lang.NoClassFoundError: HelloClient
   可能的原因是没有把“.”设置到环境变量classpath中。
   (4)执行java weblogic.ejbc... 命令时,抛出org.xml.sax.SAXParseException异常。
   原因:xml文件有语法错误
   解决办法:进行.xml文件有效性检查。
   (5)执行之后仅显示一行文字程序就结束。
   原因:可能没有部署或者没有正确部署EJB。
   解决办法:仔细查看本书3.4节的内容。

2.7 本章小结
  本章首先讨论如何进行一些准备工作,然后详细介绍了一个简单的EJB的开发过程,包括编写代码、编译、打包、部署和测试运行。通过本章的学习,以期对J2EE的开发有一个感性的认识。
   接下来的第3章到第7章将全面深入的讨论EJB应用及其开发的有关问题。
   ----
   对新手的提示:本章所称脚本文件使用的扩展名是.cmd,实际上该文件在DOS平台上扩展名为.bat,也称为批处理文件。
   本章的内容涉及到DOS环境设置,如果你想了解更多的内容可以查询相关的内容。

3.1 EJB组件
  企业Bean技术从本质上说是一个基于组件的分布式计算架构体系规范。企业Bean是分布式的面向业务逻辑的企业应用组件。
   基于组件的应用方式是把传统上的应用系统按一定的目标分成两个部分:容器和组件。这种划分更多的是从功能出发的,划分的原则把底层技术细节与应用逻辑分开。容器部分重点在于解决底层的技术问题,如目录访问、分布式事务处理、安全管理、并行访问、持久性管理、资源池管理、系统管理界面和负载均衡等方面。组件部分重点放在具体的应用逻辑上。同时,这样划分也把不同性质的开发工作分离出来。具有不同技术背景的开发人员关注于系统的某一个层面而无需对其他层面深人的了解。一旦这种划分成为了一个技术标准,那么就形成了相关的技术领域的社会工业。

3.1.1 EJB组件的特性

  企业Bean的重要特性如下:
   ·企业Bean包括操作企业数据的业务逻辑。企业Bean是用来封装业务逻辑的。
   ·企业Bean的实例被容器创建和运行。容器管理EJB。
   ·企业Bean在部署的时候通过编辑环境属性来定制。使用部署描述符是部署EJB的重要环节。
   ·不同的服务信息,如事务和安全属性,从企业Bean类中分离出来。这些通用的服务由容器一次完成。开发不同的应用时,不必再考虑这些琐碎的细节问题。用一些工具在应用程序装配和部署过程中管理服务信息。
   ·客户端访问部署在容器的组件是快速进行的。容器通过使用资源池等手段显著提高了性能。
   ·如果一个企业Bean仅使用EJB规范定义的服务,它能被部署在任何一个兼容的EJB容器中。特定的容器能提供EJB额外的服务。依赖这样服务的企业Bean只能部署在支持这个服务的容器中。EJB规范是个通用的规范,得到很多厂商的支持,确保了EJB的可移植性。
   ·企业Bean能被包含在一个已装配的应用程序中,而不需源代码的改变或重新编译。具有良好的重构性、可升级特征。
   ·Bean提供者定义一个企业Bean的客户端视图。Bean提供者可以手工定义客户端视图或通过程序部署工具自动产生。客户端视图不受Bean部署的容器和服务器的影响。这确保了Bean和它们的客户端能被部署到多种执行环境中而不需改变或重新编译。

3.1.2 EJB组件的类型

  企业Bean架构是灵活的,可以实现下列组件:
   ·表示一个无状态服务的对象。即无状态会话EJB。
   ·表示一个无状态服务对象且是异步激发的,被Java消息服务(JMS)的消息驱动。即消息驱动Bean。
   ·表示一个特定客户端的会话状态的对象。如会话对象在客户端多次调用之间自动维持它们的会话状态。即有状态会话Bean。
   ·实体对象表示商业逻辑对象,被多个客户端共享。即实体Bean。实体Beans用来描述较粗糙的逻辑对象(如订单、雇员记录等)。不太适合描述较详细的对象(如订单上的项、雇员地址等)。
   EJB的状态是个较重要的概念。这里的状态是在EJB的客户端视图定义的,也就是说EJB的状态指的是EJB呈现的客户端的状态,而并不是指EJB本身的生存状态,这两者往往并不完全一致。
   虽然EJB定义的状态管理协议简单,它为开发者提供了一个在管理状态上的企业Bean极大的灵活性。
3.1.3 企业 Beans组件应用模型
   J2EE三层应用架构定义了两种基于组建的应用模式,即Web应用和EJB应用。如表3-1所示。

         表3-1 Web应用与EJBs应用对比
   ─────────────────────────────────
         Web应用      EJBs应用
   ─────────────────────────────────
   组件    Servlets(Jsps)   EJBs
   容器    包含在Web服务器中  在应用服务器中
   通信协议  Http        Java RMI,CORBA IIOP,
                   Microsoft DCOM等
   ─────────────────────────────────
   这里我们考虑EJBS应用的情况(Web应用在第7、8章讨论)。
   如图3-1所示是EJB组件运行架构图。图中间的部分包括EJB容器和部署在容器中的组件(即EJB)。图左边的部分是EJB的客户端。图中共示意了四种客户端。EJB还有一种重要的客户端类型,就是EJB本身。也就是说,一个EJB组件可以作为另一个EJB组件的客户端。这种情况在企业级应用中是很常见的。
  


   客户端也可以简单的分成两种:即Java客户端和非Java客户端(比如C/C++)。对Java客户端来说,传输协议可以采用 Java远程方法调用(Java RMI)方式,也可以是IIOP协议,而对非Java客户端来说,只能采用IIOP协议。

3.2 EJB角色
  围绕EJB应用(包括组件和容器)的开发和部署周期,EJB规范为EJB架构定义了七个角色,每个角色由不同的单位承担。EJB规范通过定义角色者之间的“合约”,来确保角色间的正确协作。在某些时候,某个组织可以同时具有多个角色。比如某个公司会提供包含EJB服务器和容器的产品,那么它同时充当着EJB服务器提供者和容器提供者两种角色。因此,这种角色划分并不是绝对的。
   以下是EJB规范2.0中定义的EJB角色。
   1.EJB提供者
   EJB提供者,即EJB的开发者。对于多数企业级应用开发者来说,都属于这个角色。EJB提供者的产品是个包含一个或多个EJB的打包文件(即ejb-jar文件)。EJB提供者负责业务逻辑方法的 Java语言实现;定义EJB的远程和主接口方法和EJB的部署描述符。部署描述符包括EJB的结构信息(如EJB类的名字)和EJB用到的其它所用资源的描述信息。
   EJB提供者是个应用领域的专家。负责开发实现业务任务和逻辑实体的可重用的EJB。不必是系统级的程序专家,也不必考虑程序的事务并发性、安全性、分布性等问题,这些问题都是由容器解决的。
   2. EJB应用装配者
   应用装配者把EJB装配成更大的可部署的应用单元。有时这个工作也由EJB提供者完成。EJB装配者的输入是EJB提供者生产的一个或多个ejb-jar文件,输出是一个或多个包含EJB和应用装配信息的ejb-jar文件。前者ejb-jar文件的扩展名一般是JAR(Java Archive),后者ejb-jar文件的扩展名可以是WAR(Web Application Archive)或EAR(Enterprise Application Archive)。应用装配者把应用装配信息插入到部署描述符中。
   应用装配者也可以把其它应用类型的组件(如JSP,JavaServerPage)装配到EJB应用中(这就象在PetStore例子中看到的那样)。
   应用装配者工作的对象是EJB部署描述符和EJB客户端视图合约。虽然他必须熟悉EJB的远程接口和主接口,但他不必具有任何实现EJB开发的知识。
   3.EJB部署者
   部署者把从EJB提供者和应用装配者那里获取的一个或多个ejb-jar文件中EJB部署到一个指定的环境。有时这个工作也由EJB提供者完成。这个环境包括特定的EJB服务器和容器。
   部署者通常会使用EJB容器提供者提供的部署工具(如 WebLogic Server提供的weblogic.ejbc工具),来为EJB提供需要的外部资源并绑定这些资源,同时必须符合应用装配者定义的指令。
   部署者的产品是定制到特定环境的EJB,这个产品只能被部署在那个特定的容其中。也就是说,EJB的部署是与服务器平台相关的。
   部署者是应用环境的操作专家,负责与EJB部署相关的工作。如:他负责把应用装配者定义的安全角色和EJB部署环境中的用户组及账号对应起来,实现它们的映射。
   EJB容器提供者提供的部署工具用来完成部署工作,这个过程分两个阶段:
   (1)部署者首先生成附加类和接口,确保容器能管理EJB的运行。这些附加的类是和特定容器相关的。
   (2)部署者完成EJB和附加类及附加接口的安装。
   4. EJB服务器提供者
   EJB服务器提供者,即生产EJB服务器的厂家。目前符合EJB规范的服务器产品有十几种,如 WebLogic Server,Websphere,iPlanet等。EJB服务器提供者是分布式事务、分布式对象和其它低级系统级服务的专家。一个典型的EJB服务器提供者可能是操作系统生产者、中间件生产者或数据库生产者。
   目前EJB架构假定EJB服务器提供者和EJB容器提供者是同一个生产者,因此没有定义EJB服务器提供者的任何接口需求。
   5. EJB容器提供者
   容器提供者提供了:
   (1)部署EJB需要的部署工具。
   (2)EJB的运行环境。
   从EJB的角度看,容器是EJB的运行环境。容器为EJB提供了事务管理、安全管理。分布式网络管理、资源管理等服务。
   EJB架构定义的EJB容器提供者负责EJB容器和EJB服务器的实现。EJB规范没有定义EJB容器和服务器之间的接口,由厂家来实现EJB容器和服务器的划分。
   容器提供者需要系统级的编程,容器提供者的重点是开发出可伸缩的、安全的、事务处理的容器,并把它集成到EJB服务器中。容器提供者把EJB和EJB服务器分离开,并在EJB和容器之间提供了简单、标准的API。这个API就是EJB的容器合约。
   容器提供者通常会提供允许系统管理员监视和管理容器和运行在容器中的EJB的工具。
   通常把具有EJB容器、EJB服务器和Web服务器功能的产品叫做应用服务器。它为开发企业级应用提供了一个完整的平台。
   6. 管理器提供者
   在EJB的使用和开发中,经常会遇到持久性的概念。所谓持久性,是指通过辅助的永久性的存储设备,如数据库或文件等,把Java对象的状态和对应的存储设备映射起来,当Java对象的状态发生改变时,存储的映射也随之改变。即使在Java对象消失后,存储设备中依旧保存着Java对象的状态。当这个Java对象重新实例后,它就会依然保持以前的状态,这就是Java对象的持久性。
   在EJB规范中定义了具有持久性的EJB——实体EJB。根据对持久性管理方式的不同,又把实体 EJB分成容器管理持久性的 EJB(CMP EJB)和Bean管理持久性的EJB(BMP EJB)。
   一般情况下,持久性管理器包含在容器管理器中。对于EJB提供者来说,不必了解它的细节。
   对于容器管理持久性的实体EJB来说,持久性管理器负责安装在容器内的实体EJB的持久性管理。在部署阶段使用持久性管理器提供者的工具,可以生成代码,实现数据从实体EJB到数据库和应用程序的转换。
   容器管理器管理实体Bean及其用到的对象的持久性和这些对象引用的完整性。容器管理器负责容器管理持久性实体EJB的finder方法和select方法的执行。
   目前的EJB架构中没有完整定义容器和持久管理器间的接口,由厂家来自行定义。
   7.管理者
   系统管理者负责配置、企业级计算、包括EJB服务器和容器的网络设施的系统管理。同时,系统管理者监视部署在容器中的EJB的运行。
   EJB架构没有为系统管理定义合约。系统管理通常使用EJB服务器和容器提供者提供监视管理工具来完成这些工作。
   通常,应用服务器会提供系统管理的控制台工具(如 WebLogic Server中的 Console)。

3.3 EJB合约
  这里的合约是指不同EJB角色合作的约定,也是不同角色的开发者开发的产品要遵守的约定。对于EJB提供者即开发者来说,要遵守客户端视图合约;对于EJB容器的开发者和使用者来说,要遵守组件和约。本节对这两个合约进行讨论。
   在EJB开发过程中,这些合约的体现是EJB开发接口和一些EJB内部对象。
   1. 客户端视图合约
   是客户端与容器之间的合约。客户端视图合约为使用企业Beans作为组件开发应用程序提供一个统一的开发模式。这个统一的模型使使用高级的开发工具成为可能,并实现组件重用。企业Bean客户端视图是基于远程访问的,它允许本地和远程对象采用统一的方式进行访问。
   一个企业Bean的客户端可能是部署在同一个或不同容器的另一个企业Bean。也可以是任何一个Java程序,如应用程序、Applet或Servlet。一个企业Bean的客户端视图也可以被映射到非Java客户端,如CORBA客户端。它不是由JavaTM编程语言写的。企业Bean提供者和容器提供者一同提供企业Bean的客户端视图。客户端视图包括:
   ·主程接口(Home Interface)
   ·远程接口(Remote Interface)
   ·对象识别(Object Identity)
   ·元数据接口(Metadata Interface)
   ·句柄(Handle)
   主接口(Home Interface):定义创建、删除和查找同类 EJB对象的方法(即它们由同一个企业Bean实现)和主业务逻辑方法(即不是特定某个Bean实例的方法)。主接口由Bean提供者制订;容器创建一个类来实现主接口。主接口继承javax.ejb.EJBHome接口。
   客户端通过标准的Java名称和目录接口(Java Naming and Directory Interface TM,JNDI)API定义企业Bean主接口。
   EJB对象是通过远程接口(Remote Interface)来访问的。远程接口定义了客户端调用的业务逻辑方法。远程接口由Bean提供者制定;容器创建一个类来实现远程接口。远程接口继承了javax.ejb.EJBObject接口。javax.ejb.EJBObject接口定义了允许客户端访EJB对象标识和EJB对象创建一个持久性句柄的操作。
   每个EJB对象在主域里存活,并有一个惟一的标识。对 Session Beans来说,容器负责为每个Session对象产生新的惟一标识。这个标识并没有暴露给客户端。然而,客户端可以测试两个对象引用是否指向同一个Session对象。对实体EJB来说,Bean提供者负责在Entity对象创建的时候提供一个主键;容器使用主键来在主域中标识实体对象。客户点可以通过javax.ejb.EJBObject接口获取一个实体对象的主键。客户端也可以测试两个对象引用是否指向同一个实体对象。
   客户端也可以获取企业Bean的元数据接口。客户端使用元数据接口主要用在执行企业Bean的动态调用上。(提供客户端视图的类不可用的时候需要动态调用)。
   消息驱动Beans没有Home和Remote接口,因此它没有客户端视图。客户端可以通过标准JNDI API定位发到消息驱动Bean的消息所在的JMS目标。
   2. 组件合约
   这部分描述企业Bean和它的容器之间的合约,以及在容器管理持久性的企业Bean情况下,企业Bean和它的持久管理器之间的合约。主要的合约如下(部分):
   ·需要Bean提供者实现企业Bean类中的业务方法。需要容器提供者提供客户端调用这些方法的代理。
   ·对于消息驱动Beans来说,需要Bean提供者实现企业Bean类中的onMessage方法。需要容器提供者调用这些方法。
   ·如果Bean是实体并且是Bean管理持久性(BMP)的,需要Bean提供者实现ejbCreate方法ejbPostCreate方法、ejbRemove方法和ejbFind方法。需要容器提供者在EJB对象的创建、删除和查询的过程中调用这些方法。
   ·对于容器管理的持久性,需要Bean提供者为持久属性提供抽象的访问方法和一个实体与容器管理持久性的关系。需要持久管理提供者提供这些方法的实现。
   ·需要Bean提供者提供持久性管理器实现的关系描述。
   ·需要Bean提供者定义企业Bean的主接口和远程接口,如果是实体Bean或Session Bean,需要Container提供者提供实现这些接口的类。
   ·对会话来说,需要Bean提供实现javax.ejb.SessionBean接口或在javax.ejb.SessionSynchronization接口中定义的容器回调。需要容器管理者在适当的时候调用这些回调。
   ·对消息驱动Beans来说,需要Bean提供者实现定义在接口javax.ejb.MessageDrivenBean中的容器回调。
   ·对实体EJB来说,需要Bean提供者实现定义在javax.ejb.EntityBean中的容器回调。需要容器在适当的时候调用这些回调。
   ·需要持久管理器提供者实现实体Beans的持久性。
   ·需要容器提供者为会话bean实例提供javax.ejb.SessionContext接口,为实体bean实例提供javax.ejb.EntityContext接口,为消息驱动bean实例提供javax.ejb.MessageDrivenContext接口。上下文接口允许实例获取容器的信息。
   ·需要容器为Bean实例提供JNDI上下文,它包含企业Bean的环境信息。
   ·需要容器为企业Bean实例管理事务、安全和异常。
   ·需要Bean提供者避免程序影响企业Bean实例的容器运行时管理。
   3. ejb-jar文件
   一个ejb-jar文件具有标准的格式。EJB工具用它来作为描述性的语言打包企业Beans。ejb-jar文件用来执行程序的装配和部署。
   ejb-jar文件包括:
   ·企业Beans的Java类文件和它们的远程和主接口。
   ·XML部署描述符。部署描述符提供关于在ejb-jar文件中的企业Beans的结构和装配信息。
   4. 合约总结
   如图3-2描述了EJB的合约。
  


   注意 图3-2只显示远程客户端运行在Container外的情况,客户端视图API也可以用在同一个容器中的企业Beans。

3.4 会话、实体和消息驱动Bean对象

   EJB架构定义了三种类型的企业Bean对象:
   ·会话对象(Session Obect)
   ·实体对象(Entity Obect)
   ·消息驱动对象(Message-Driven Object)
   1. 会话对象(Session)
   一个典型的会话对象具有如下特征:
   ·由一个客户端执行。
   ·自动事务感知。
   ·在一个潜在的数据库中更新共享数据。
   ·不直接表示数据库中的共享数据,虽然它可以访问和更新这些数据。
   ·相对来说是短命的。
   ·当EJB容器停止后被清除掉。客户只能重新建立新的对象来继续计算。
   一个典型的EJB Containe提供一个灵活的运行环境来并发的执行大量的会话对象。
   会话Beans是有状态的。同时EJB规范也支持无状态会话Bean作为一个特殊的情况。有状态(正常)会话Beans和无状态会话Bean的API有很小的差别。
   2. 实体对象(Entity)
   一个典型的实体对象有如下特征;
   ·提供一个数据库的数据视图。
   ·允许多个用户共享访问。
   ·存活时间长(和数据库中的数据一样)。
   ·实体,主键和远程引用可以在EJB容器停止后存活。如果实体的状态被事务更新,同时容器停止,实体的状态自动地重新置为最后一次触发事务的状态。停止对于客户端来说不是完全透明的,在它调用一个实体容器崩溃的时候,客户端可能会收到一个异常。
   特殊的EJB容器和服务器为多个并发的实体对象提供一个可变的运行时环境。
   3. 消息驱动对象
   一个典型的消息驱动对象具有如下特征:
   ·收到某个客户端消息时执行。
   ·自动事务感知。
   ·自动更新共享数据。
   ·不直接代表数据库中的共享数据,虽然它可以访问和更新这些数据。
   ·生命短。
   ·无状态。
   ·EJB容器崩溃时被删除。容器不得不重新建立新的消息驱动对象来继续计算。
   一个典型的EJB容器提供一个灵活的运行环境来同时执行多个消息驱动对象。

3.4 会话、实体和消息驱动Bean对象

   EJB架构定义了三种类型的企业Bean对象:
   ·会话对象(Session Obect)
   ·实体对象(Entity Obect)
   ·消息驱动对象(Message-Driven Object)
   1. 会话对象(Session)
   一个典型的会话对象具有如下特征:
   ·由一个客户端执行。
   ·自动事务感知。
   ·在一个潜在的数据库中更新共享数据。
   ·不直接表示数据库中的共享数据,虽然它可以访问和更新这些数据。
   ·相对来说是短命的。
   ·当EJB容器停止后被清除掉。客户只能重新建立新的对象来继续计算。
   一个典型的EJB Containe提供一个灵活的运行环境来并发的执行大量的会话对象。
   会话Beans是有状态的。同时EJB规范也支持无状态会话Bean作为一个特殊的情况。有状态(正常)会话Beans和无状态会话Bean的API有很小的差别。
   2. 实体对象(Entity)
   一个典型的实体对象有如下特征;
   ·提供一个数据库的数据视图。
   ·允许多个用户共享访问。
   ·存活时间长(和数据库中的数据一样)。
   ·实体,主键和远程引用可以在EJB容器停止后存活。如果实体的状态被事务更新,同时容器停止,实体的状态自动地重新置为最后一次触发事务的状态。停止对于客户端来说不是完全透明的,在它调用一个实体容器崩溃的时候,客户端可能会收到一个异常。
   特殊的EJB容器和服务器为多个并发的实体对象提供一个可变的运行时环境。
   3. 消息驱动对象
   一个典型的消息驱动对象具有如下特征:
   ·收到某个客户端消息时执行。
   ·自动事务感知。
   ·自动更新共享数据。
   ·不直接代表数据库中的共享数据,虽然它可以访问和更新这些数据。
   ·生命短。
   ·无状态。
   ·EJB容器崩溃时被删除。容器不得不重新建立新的消息驱动对象来继续计算。
   一个典型的EJB容器提供一个灵活的运行环境来同时执行多个消息驱动对象。

3.4 会话、实体和消息驱动Bean对象

   EJB架构定义了三种类型的企业Bean对象:
   ·会话对象(Session Obect)
   ·实体对象(Entity Obect)
   ·消息驱动对象(Message-Driven Object)
   1. 会话对象(Session)
   一个典型的会话对象具有如下特征:
   ·由一个客户端执行。
   ·自动事务感知。
   ·在一个潜在的数据库中更新共享数据。
   ·不直接表示数据库中的共享数据,虽然它可以访问和更新这些数据。
   ·相对来说是短命的。
   ·当EJB容器停止后被清除掉。客户只能重新建立新的对象来继续计算。
   一个典型的EJB Containe提供一个灵活的运行环境来并发的执行大量的会话对象。
   会话Beans是有状态的。同时EJB规范也支持无状态会话Bean作为一个特殊的情况。有状态(正常)会话Beans和无状态会话Bean的API有很小的差别。
   2. 实体对象(Entity)
   一个典型的实体对象有如下特征;
   ·提供一个数据库的数据视图。
   ·允许多个用户共享访问。
   ·存活时间长(和数据库中的数据一样)。
   ·实体,主键和远程引用可以在EJB容器停止后存活。如果实体的状态被事务更新,同时容器停止,实体的状态自动地重新置为最后一次触发事务的状态。停止对于客户端来说不是完全透明的,在它调用一个实体容器崩溃的时候,客户端可能会收到一个异常。
   特殊的EJB容器和服务器为多个并发的实体对象提供一个可变的运行时环境。
   3. 消息驱动对象
   一个典型的消息驱动对象具有如下特征:
   ·收到某个客户端消息时执行。
   ·自动事务感知。
   ·自动更新共享数据。
   ·不直接代表数据库中的共享数据,虽然它可以访问和更新这些数据。
   ·生命短。
   ·无状态。
   ·EJB容器崩溃时被删除。容器不得不重新建立新的消息驱动对象来继续计算。
   一个典型的EJB容器提供一个灵活的运行环境来同时执行多个消息驱动对象。

3.5 CORBA协议的标准映射
  为了实现不同系统的EJB环境间的协作,EJB 2.0规范需要会话Beans和实体Beans的J2EE客户调用端支持基于CORBA/IIOP协议兼容的实现。

3.6 本章小结
  对于本章阐述的概念,读者不可能在看过一次后完全领会。本书后面几章提供了大量的EJB应用的实例,读者在实际操作完这些例子后再重新理解这些概念会取得更好的效果。
   下一章讨论会话EJB的特征、编程模型和实例开发。


本章讨论的内容包括:什么是会话EJB,两种会话EJB以及它们的区别、特性和应用场合,会话EJB的编程模型。并提供了两种会话EJB的开发实例。
  4.1 什么是会话EJB
  会话EJB实现了运行于服务器上的业务逻辑。对客户端来说,一个会话对象是一个非持久性的对象。对会话对象的一种理解是把它看成运行在服务器上的客户端程序的扩展。
   会话EJB一个最显著的特征是它是依附于客户端的。从会话EJB的产生和销毁都由客户端控制。会话EJB的状态不会被永久保存。也就是说,会话EJB不具备持久性。
   会话EJB在实际应用中的一个典型例子是订单的处理。例如对于某个登录的用户,他进行订单的操作由若干步骤完成,在所有步骤完成之前,并不是把每个步骤的操作都记录到永久性的存储器(如数据库)中,而是由会话EJB暂保存这些状态数据,并完成所有步骤之后,再把所有的状态数据提交到相应的处理模块。另一个类似的例子是宠物商店中的购物车(关于宠物商店的详细介绍参见第14章),用户把选购的商品放入购物车中,选完所有商品后才一起结账,一个购物车可以设计成一个会话EJB。
   会话EJB的状态指的是会话EJB所保存的某个客户端的状态。有状态会话EJB指的是能够保持某个客户端状态的会话EJB;无状态会话EJB指的是不保存某个客户端状态的会话EJB。因此,从这个意义上说,无状态会话EJB并不是完全的没有状态,它只是没有保存特定客户端的状态而已。无状态会话EJB适用在不保持客户端的状态和状态不随客户端的不同而不同的情况。例如计算一个实数的平方根(不保存状态);有状态会话EJB的例子是把商品类型的数据库映射成无状态EJB(对于不同客户端,商品类型是一致的)。很多资料认为会话EJB是有状态的,而把无状态会话EJB看做是它的一个特殊情况。之所以采用无状态会话EJB。仅仅是为了在特殊的情况下,提高会话EJB的效率。也就是说,无状态会话EJB从响应速度和占用资源方面比有状态会话EJB效率高。
   在下面的章节中会进一步讨论两种会话EJB的异同。

4.2 会话EJB编程模型
  如图4-1所示,会话EJB的编程模型简述如下:客户端通过Bean的远程接口访问会话对象。而实现这个远程接口的Java对象是一个会话对象EJBObject。一个EJBObject会话对象是一个能被客户端通过标准的远程方法调用Java API访问的远程Java对象。从创建到销毁,会话对象存在于容器中。容器对客户端来说是透明的,容器为Session对象提供了安全、并发、事务、缓存和其他的服务,每个Session对象拥有一标识。一般情况下,该标识随着服务器的停止而结束。Session Bean的客户端视图与位置无关,一个和Session对象运行在同一个JVM的客户端和运行在相同或不同机器上的不同JVM的客户端使用相同的APIs。一个会话对象的客户端可能是另外一个部署在相同或不同容器上的企业Bean,或者是任意的Java程序,如应用程序、Applet,或Servlet。一个Session Bean的客户端视图也可以映射成非Java客户端环境,如CORBA客户端,这不是由Java程序语言写的。
  


   多企业Bean可以安装在一个容器中。容器允许客户端通过JNDI查找安装好的主接口。一个Session Bean的主接口提供了这个Bean创建、删除的方法。一个会话对象的客户端视图是一致的,它不随session Bean和容器的不同而不同。
   下面深入讨论其中相关的问题。

4.2.1 定位Bean的主接口

  多个EJB可以部署在同一个容器中。客户端可以使用JNDI查找指定企业Bean的主接口。
   客户端使用JNDI来查找会话Bean的主接口。例如,一个Cart会话Bean的主接口可以使用如下代码查找。
   Context initialContext = new InitialContext();
   CartHome cartHome = (CartHome)javax.rmi.PortableRemoteObject.narrow(
    initialContext.lookup("java:comp/env/ejb/cart"),CartHome.class);
   一个客户端的JNDI命名空间可以配置起来包含网络上在不同机器上的、不同EJB容器中的EJB的主接口。而EJB容器的实际位置对使用企业Bean的客户端来说是透明的。
   实现主接口的分布式对象(即EJBHome对象)的生命周期是与EJB容器相关的。客户端应用程序可以获取主接口,而且在客户端的生命周期中多次使用。客户端可以把主接口的引用传递给另一个应用程序,而接受的应用程序可以使用主接口对象的引用,就和通过JNDI获取的一样。
   容器实现了安装在容器中的EJB的主接口。实现会话Bean主接口的对象叫做EJBHome对象。容器使会话Bean的主接口通过JNDI对客户端可用。

   主接口允许客户端进行如下操作。
   ·创建一个新的会话对象
   ·删除一个会话对象
   ·为会话Bean获取javax.ejb.EJBMetaData接口
   javax.ejb.EJBMetaData接口使应用程序装配工具发现关于Session Bean的信息,并允许松散的Client/Server绑定客户端脚本。
   ·获取主接口的一个句柄
   主句柄是可序列化的,能写入到固定的存储器。然后,句柄可以在另一个不同的JVM中,从固定存储器中反序列化以获取主接口的引用。
4.2.2 创建并使用会话对象

  主接口定义一个或多个create(...)方法,每个方法定义了一种创建会话对象的方式,create方法的参数用来初始化创建的会话对象的状态。
   下面这个例子演示了有两个create<METHOD>(...)方法的主接口:
   public interface CartHome extends javax.ejb.EJBHome{
    Cart create(String customerName,String account)
     throws RemoteException,BadAccountException,CreateException;
   Cart createLargeCart(String customerName,String account)
    throws RemoteException,BadAccountException,CreateException;}
   下面演示了客户端怎样使用主接口的create<METHOD>(...)方法创建远程对象:
   cartHome.create("John","7506");
   客户端从来不会直接访问会话Bean类的实例。客户端使用会话Bean的远程接口来访问会话Bean的实例。实现会话Bean的远程接口的实例由容器提供,它的实例叫做会话EJBObjects。
   会话EJBObject支持:
   ·对象的业务逻辑方法
   会话EJBObject代理对会话Bean实例的业务逻辑方法的调用。
   ·javax.ejb.EJBObject接口定义的方法。这些方法允许客户端:
   ·获取会话对象的主接口
   ·获取会话对象的句柄
   ·测试会话对象是否与另一个是同一个
   ·删除会话对象
   javax.ebj.EJBObject接口中定义的方法是由容器实现的,而并不是由会话Bean类代理的。
   一个会话Bean运行期对象的例子如图4-2所示。
   客户端使用Cart主接口的create方法创建一个Cart会话对象。客户端使用这个会话对象来填写购物单并购买相关内容。
   假设用户开始购买后终止一两天,然后再去完成.客户端可以通过获取会话对象的句柄,把句柄序列化到持久性的存储器中,然后重新用它来创建最初的Cart来实现。
   对下列例子,开始,在JNDI中查找Cart的主接口,然后使用主接口来创建一个Cart会话对象并加入一些购物项:
   CartHome cartHome = (CartHome)javax.rmi.PortableRemoteObject.narrow{
   initialContext.lookup(...),CartHome.class);
   Cart cart = cartHome.createLargeCart(...);
   cart.addItem(66);
   cart.addItem(22);
   下面我们决定稍候完成这次购物,因此要序列化这个Cart会话对象的句柄并把它存在一个文件里:
   Handle cartHandle = cart.getHandle();
   //serialize cartHandle,store in a file...
   最后我们从文件中反序列化句柄,并重新创建cart会话对象的引用,并购买购物单里的商品:
   Handle cartHandle = ...;//deserialize from a file...
   Cart cart = (Cart)javax.rmi.PortableRemoteObject.narrow(
   cartHandle.getEJBObject(),Cart.class);
   cart.purchase();
   cart.remove();

4.2.3 删除会话对象

  客户端可以使用javax.ejb.EJBObject接口的remove()方法删除一个会话Bean,或者使用接口javax.ejb.EJBHome中定义的remove(Handle hanele)方法。
   因为会话对象没有主键供客户端访问,调用方法javax.ejb.EJBHome.remove(Object primaryKey)会抛出异常javax.ebj.RemoveException。
   拥有会话引用的客户端可以做如下事情:
   ·调用会话对象远程接口中定义的业务方法
   ·获取会话对象主接口的引用
   ·获取会话对象的句柄
   ·在客户端的范围内把引用作为参数或返回值
   ·删除会话对象,会话对象也可以在生命周期到时被容器自动删除。
   引用一个不存在的会话对象是无效的。试图调用一个存在的会话对象会抛出异常:java.rmi.NoSuchObjectException。

4.2.4 会话标识

  会话对象作为创建它的客户端的私有资源,在客户端看来是匿名的。和实体对象相反,实体对象有主键作为标识,会话对象隐藏它们的标识。结果是,调用会话Bean的EJBObject.getPrimaryKey()方法和EJBHome.remove(Object primaryKey)方法会抛出java.rmi.RemoteException异常。
   如果调用会话Bean的EJBMetaData.getPrimaryKeyClass()方法,会抛出异常java.lang.RuntimeException。
   既然所有的会话隐藏了它它们的标识,就不需要为它们提供finder方法。因此,会话Bean的主接口没有定义任何finder方法。
   会话对象句柄在客户端生命结束后可以继续保持,可以把它序列化到一个持久的存储器中。当句柄被反序列化后,会话对象可以正常工作,只要服务器上的会话对象依然存在(时间到或服务器关闭会销毁会话对象)。
   客户端代码必须使用javax.rmi.PortableRemoteObject.narrow(...)方法来把调用句柄上的getEJBObject()方法的结果转化成远程接口类型。
   自动允许句柄的所有者来调用远程对象的方法。当会话对象的引用从一个句柄中获得,并当会话对象的方法被调用时,容器会基于调用的原则进行访问检查。
   1. 有状态会话EJB标识
   有状态会话对象具有惟一的标识,在容器创建的时候被分配。
   客户端通过调用isIdentical(EJBObject otherEJBObject)方法能判断两个会话对象引用是否一致。
   下面演示了有状态会话对象中isIdentical方法的使用:
   FooHome fooHome = ...;//obtain home of a stateful session bean
   Foo foo1 = fooHome.create(...);
   Foo foo2 = fooHome.create(...);
   if (foo1.isIdentical(foo1)){//返回true
   ...
   }
   if (foo1.isIdentical(foo2)){//返回false
   ...
   }
   2. 无状态会话EJB标识
   同一标识对象创建的所有无状态会话Bean拥有同样的标识,这个标识是容器分配的。如果无状态会话EJB被部署了多次(每次部署导致一个不同的home的创建),不同的主接口的会话对象就会有不同的标识。
   当用来比较同一个无状态什么Bean的不同对象应用时,方法isIdentical(EJBObject otherEJBObject)总会返回true。
   下面例子演示了stateless session object的isIdentical方法的使用。
   FooHome fooHome = .../obtain home of a stateless session bean
   Foo foo1 = fooHome.create();
   Foo foo2 = fooHome.create();
   if (foo1.isIdentical(foo1)){//返回true
    ...
   }
   if (foo1.isIdentical(foo2)){//仍然返回true
    ...
   }
   3. getPrimaryKey()
   会话对象的标识对客户端来说是透明的,调用会话EJBObject的getPrimaryKey()方法会抛出java.rmi.RemoteException异常。

http://www.51testing.cn/html/20/97.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: