解决方案

EJB的简单介绍和使用

seo靠我 2023-09-25 22:19:16

17.1 为什么需要EJB

要想知道为什么要使用EJB,就需要知道"面向服务"的概念。"面向服务",是软件开发过程中,异构环境下模块调用的一个比较重要的思想。同样,面向服务也只是一种设计思想,不是一种编SEO靠我程技术。由"面向服务"的思想,业界提出了"面向服务的体系结构(Service Oriented Architecture, SOA)"的概念。

用一个实际案例来引入"面向服务"的概念。在某些大型应用场合SEO靠我,我们要在不同的运行环境之间传递数据,比如:

A公司需要从B公司的数据库中查询一些内容之后返回,进行处理,如何实现?

最简单的结构,如图17-1所示:

图17-1 最简单的两公司之间互相调用的结构 SEO靠我 但是,以上程序在实际操作中,是不能实现的。因为JDBC代码写在A公司部分,那就必须让A公司的程序知道B公司数据库的详细结构。在一般情况下,这是不合理的。比如,一个公司通过自己的平台向银SEO靠我行转账,不可能知道银行数据库的结构。于是,程序可以变为如图17-2所示结构:

(点击查看大图)图17-2 改进的结构

该结构详述如下:B公司编写自己的程序,访问数据库,对外发布一个接口,并发布一个服务的名SEO靠我称。我们知道,接口里面并没有核心代码。该接口也被A公司获取,A公司网上寻找相应的B公司发布的服务名称,然后通过接口调用B公司程序里面的方法。

但是,该技术不是简单就可以实现的,因为A公司和B公司的程序,SEO靠我可能运行在不同的虚拟机内,甚至可能是不同的语言。EJB可以解决A公司和B公司使用的都是Java语言,但是处于不同的Java虚拟机的情况。

该问题的原型是:一个Java虚拟机内的对象能否远程调用另外一个JSEO靠我ava虚拟机里面的对象内的方法?实际上,在Java内,该技术可以用RMI(远程方法调用)实现。而EJB的底层,就是用RMI实现的。

实际上,即使是在同一个Java虚拟机内,将某个功能以服务的形式对外发布SEO靠我,被该虚拟机中的另一个模块调用,也是可以大大降低耦合性的。因为模块之间打交道的,只是一个接口和一个服务名称。

不过,顺便需要提到的是,如果两个程序使用的是不同语言平台,如一个是C,一个是Java,业界中SEO靠我也提出了一些方法来解决数据交换问题,如WebService、CORBA等。读者可以参考相关文献。

17.2 EJB框架的基本原理

17.2.1 EJB框架简介

如前所述,EJB实际上是服务器端运行的一个对象SEO靠我,只不过该对象所对应的类并不被客户端所知,该对象对外发布的是一个服务名称,并提供一个可以被客户端调用的接口。通俗点说,EJB就是一个可以被客户端调用,但是并不让客户端知道源代码的类的对象。

因此,EJBSEO靠我并不是普通的Java Bean,普通的JavaBean是一个符合某种规范的Java类文件,只能作为一个类被调用,只有调用的时候才运行,是一个进程内组件。而EJB并不是一个单独的文件,其组成包括:

1. SEO靠我类文件:实现基本方法的类,封装了需要实现的商务逻辑,数据逻辑或消息处理逻辑,具有一定的编程规范,代码不能被客户端得知。

2. 接口文件:接口是EJB组件模型的一部分,里面提供的方法一般和需要被远程调用的SEO靠我方法一致,一般情况下,要求类文件必须和接口中的定义保持一致性。

3. 必要的情况下,编写一些配置文件,用于描述EJB部署过程中的一些信息。

EJB可以作为一个服务被调用,可以单独运行,是一个进程级组件。ESEO靠我JB中还提供了一些安全管理、事务控制功能,使得我们调用EJB时,不需要太多地束缚于这些问题的编码。

EJB 定义了四种类型的组件:

1. Session Bean:会话Bean,封装业务逻辑,负责完成某个SEO靠我操作。根据生命周期的不同,又可以分为:

(1) Stateless Session Bean: 无状态会话Bean,不存储用户相关信息,一般说来,在服务器端,一个Bean对象可能为很多客户服务,如图17SEO靠我-3所示:

图17-3 无状态会话Bean的使用

由于一个Bean对象可能为多个客户服务,因此,一般不在对象内保存某个客户的状态,保存也没有意义。

(2) Stateful Session Bean: 有状SEO靠我态会话Bean,可以存储用户相关信息,在服务器端,一个Bean对象只为一个客户服务,如图17-4所示:

图17-4 有状态会话Bean的使用

由于一个Bean对象只为一个客户服务,因此,可以在对象内保存某SEO靠我个客户的状态。

2. Entity Bean:实体Bean,类似Hibernate,封装数据库中的数据,代表底层数据的持久化对象,把表中的列映射到对象的成员,主键在实体Bean中具有唯一性,一个实体BeSEO靠我an对象对应表中的一行,这将在下一章讲解。

3. Message Driven Bean:消息驱动Bean,是一种异步的无状态组件,和无状态会话组件具有相似性,是JMS消息的消费者,可以和JMS配合起来SEO靠我使用。

17.2.2 EJB运行原理

本章所讲解的EJB,特指会话Bean。

在EJB中,常用的的组件有:客户端、接口(远程接口或者本地接口)、EJB实现类、JNDI名称等。它们之间的关系如图17-5所示:SEO靠我

图17-5 EJB组件之间的关系

对于一个业务操作,其执行步骤为:

首先,服务器端将EJB发布为一个JNDI名称,并提供一个接口文件。不过,值得注意的是,如果客户端和EJB运行在同一个容器内,可以提供的是SEO靠我本地(Local)接口,如果运行在不同的Java虚拟机内,提供的是远程(Remote)接口。接下来步骤如下:

1. 客户端向服务器发起连接,在服务器上寻找相应的JNDI名称,如果找到,返回一个对象。

2.SEO靠我 客户端将该对象强制转换为接口类型。

3. 客户端调用接口中的方法,实际上调用了服务器端EJB内的方法。

因此,利用EJB编程,有以下几个步骤:

1. 编写EJB实现类。

2. 编写接口。

3. 部署到服务器中SEO靠我,设定JNDI名称。

4. 编写客户端,并将接口拷贝给客户端,将JNDI名称公布,客户端调用EJB。

17.3 EJB框架的基本使用方法

该部分内容使用实际案例进行讲解。以一个银行系统为例,银行系统中提供一SEO靠我个"根据美元计算人民币"的功能,我们知道,美元必须乘以相应的汇率才能得到人民币,而汇率可能保存在银行的数据库中,该数据库结构不能对外公开。因此,客户端必须在不知道数据库结构的情况下,调用银行系统中"根SEO靠我据美元计算人民币"的方法,这就可以使用EJB实现。

本例中,需要建立远程接口和实现类。因为"根据美元计算人民币"的方法,可能是被远程调用的。

17.3.1 建立EJB项目

接下来就开始编写这个项目,打开MySEO靠我Eclipse,新建一个EJB项目,如图17-6所示:

图17-6 新建EJB项目 弹出"New EJB Project"对话框,确定项目名称。注意,"J2EE SpecificatioSEO靠我n Level"中一定要选定"Java EE5.0 - EJB3",否则无法支持EJB3。把界面下方的其他勾选去掉。如图17-7所示: 图17-7 新建EJB项目

如前所述,我们需要建立SEO靠我Bean的实现类和Bean的接口,由于接口最终需要被客户端使用,因此,适合单独放在一个包内。此处,可以在该项目中建立接口所在包:itf;以及实现类所在的包:impl。注意,此处的命名可能不一定规范,但SEO靠我是主要是为了便于理解,说明问题。建立好的项目如图17-8所示:

图17-8 新建EJB项目结构

17.3.2 编写远程接口

远程接口提供了客户端和服务器端的通信桥梁,在里面只有一个函数,就是可能被远程调用的SEO靠我函数。代码如下:

Convert.java

package itf;  public interface Convert {      public String getRmb(String usd); SEO靠我 }

很显然,该代码非常简单。该代码被客户端使用,也很方便。

17.3.3 编写实现类

Bean的实现类运行在服务器端,包含了核心代码。在"由美元计算人民币"的方法中,本来需要查询服务器端的数据库,为了简单SEO靠我起见,我们给定一个汇率值,不影响知识的理解。代码如下:

ConvertBean.java

package impl;  import itf.Convert;  public class ConvertBSEO靠我ean implements Convert {      public String getRmb(String usd){          //从数据库查询汇率,此处简化,假如汇率是6.0   SEO靠我       double rate = 6.0;          double dblUsd = Double.parseDouble(usd);          double dblRmb =SEO靠我 dblUsd * rate;          String rmb = String.valueOf(dblRmb);          return rmb;      }  }

该代码很简单,BSEO靠我ean的实现类,实现了相应的接口。

17.3.4 配置EJB

编写了EJB实现类,还无法确定该EJB是否能够被远程调用,并且无法确定该会话Bean是有状态的还是无状态的。因此,需要进行配置。

在较早版本的ESEO靠我JB中,需要进行比较复杂的配置,编写xml配置文件,在EJB3中,你可以选择编写配置文件,也可以将配置在代码中标明。方法是:修改ConvertBean的源代码:

ConvertBean.java

packSEO靠我age impl;  import itf.Convert;  import javax.ejb.Remote;  import javax.ejb.Stateless;  @Stateless (mSEO靠我appedName="ConvertBean")  @Remote  public class ConvertBean implements Convert {      public String SEO靠我getRmb(String usd){          //从数据库查询汇率,此处简化,假如汇率是6.0          double rate = 6.0;          double dbSEO靠我lUsd = Double.parseDouble(usd);          double dblRmb = dblUsd * rate;          String rmb = StringSEO靠我.valueOf(dblRmb);          return rmb;      }  }  注意,在该代码类定义之前,定义了: @Stateless (maSEO靠我ppedName="ConvertBean")  @Remote

表示:

1. 确定该EJB是可以被远程调用的。

2. EJB的JNDI名称为"ConvertBean",客户端寻找该EJB时,所使用的名字为SEO靠我"ConvertBean#itf.Convert",实际上是相当于寻找里面的接口。注意,在不同厂商的服务器中,JNDI格式有所不同。

2. 该EJB是无状态的会话Bean。

编写完毕,项目结构如图17-9SEO靠我所示:

图17-9 EJB项目结构

17.3.5 部署EJB

接下来就是将EJB部署到服务器中去。默认情况下,Tomcat不支持EJB,支持EJB的服务器有WebLogic、WebSphere、JBoss等SEO靠我,此处我们使用WebLogic10,以及其内部配置的用户服务器域:base domain,并已经在MyEclipse中对其进行了配置绑定。具体安装过程,参考第1章的内容。

点击工具条上的"部署"按钮,如SEO靠我图17-10所示:

图17-10 部署按钮 打开部署窗口,如图17-11所示: (点击查看大图)图17-11 部署窗口 选择项目名SEO靠我称,点击"Add"按钮,出现如图17-12所示的界面:

(点击查看大图)图17-12 部署窗口

在该窗口中,选择"WebLogic 10.x",在下方选择"以目录形式部署"或者"以压缩包形式部署",系统将SEO靠我会在最下面显示部署的路径。此处选择"以目录形式部署"。完成。

接下来运行WebLogic服务器。如果MyEclipse和WebLogic已经绑定(参考第1章),工具条上会出现WebLogic服务器的打开SEO靠我菜单,如图17-13所示:

(点击查看大图)图17-13 打开WebLogic 可以打开WebLogic。打开之后,在浏览器中输入: http://locSEO靠我alhost:7001/console ,打开控制台,输入账号密码,登录,进入控制台,在"Domain Structure"中,点击"Deployments",如图17-14所SEO靠我示:

图17-14 Domain Structure 在界面右方显示如图17-15所示:

(点击查看大图)图17-15 显示EJB 点击该EJB链接最左边的SEO靠我"+"号,出现如图17-16所示的界面,显示了EJB详细信息:

图17-16 EJB详细信息

该详细信息中,在"EJBs"下,名称"ConvertBean",注意,这并不是JNDI名称,知识该EJB实现类SEO靠我的类名称。

17.3.6 远程调用该EJB

该EJB被部署之后,就可以被远程调用了。很明显,要想远程调用该EJB,必须满足:

1. 得知服务器是WebLogic,因为不同的服务器连接方式可能不一样。

2. 得SEO靠我知服务器的IP地址和端口。

3. 拥有该EJB的远程接口的class文件,得知服务器端EJB的JNDI名称,如前所述,名称为:"ConvertBean#itf.Convert"。

建立普通的项目Prj17SEO靠我_Test,将远程接口拷贝到该项目中去,并且建立一个TestConvert.java,项目结构如图17-17所示:

图17-17 项目结构

编程步骤如下:

1. 确定连接目标:

……  Hashtable tSEO靠我able = new Hashtable();  table.put(Context.INITIAL_CONTEXT_FACTORY,  "weblogic.jndi.WLInitialContextSEO靠我Factory");  table.put(Context.PROVIDER_URL,"t3://localhost:7001");  ……

注意,此处用到了weblogic.jndi.WLInitiaSEO靠我lContextFactory,是WebLogic中专门负责初始化上下文对象的类,因此,本项目中,需要导入WebLogic相关开发包。方法是:右击项目名称,选择"Properties",如图17-18SEO靠我所示:

图17-18 选择项目属性 在跳出的窗口中,找到"Java Build Path",切换到"Library",如图17-19所示:

(点击查看大图)图17-19 属性窗口

SEO靠我击"Add External JARs",找到%WebLogic安装目录%/server/lib/weblogic.jar,导入。如图17-20所示:

(点击查看大图)图17-20 导入效果 SEO靠我 2. 查询服务器中的JNDI名称:

……          Context context = new InitialContext(table);          Convert coSEO靠我nvert = ( Convert) context.lookup(jndiName);  ……  3. 调用接口: ……          String rmb =SEO靠我 convert.getRmb(usd);          System.out.println(rmb);  ……

整个文件的代码为:

TestConvert1.java  import itf.CoSEO靠我nvert;  import java.util.Hashtable;  import javax.naming.Context;  import javax.naming.InitialContexSEO靠我t;  public class TestConvert1 {      public static void main(String[] args) throws Exception{       SEO靠我   String usd = "1234";          String jndiName = "ConvertBean#itf.Convert";          Hashtable tabSEO靠我le = new Hashtable();  table.put(Context.INITIAL_CONTEXT_FACTORY,  "weblogic.jndi.WLInitialContextFaSEO靠我ctory");          table.put(Context.PROVIDER_URL,"t3://localhost:7001");          //查询服务器中的jndiName SEO靠我         Context context = new InitialContext(table);          Convert convert = ( Convert) context.SEO靠我lookup(jndiName);          String rmb = convert.getRmb(usd);          System.out.println(rmb);      SEO靠我}  }

运行,显示的效果如图17-21所示:

图17-21 显示效果

说明可以正常运行。

从此处可以看出,客户端没有知道服务器端的任何源代码,就可以调用服务器端的EJB对象。

17.3.7 无状态会话BeanSEO靠我的生命周期

接下来讲解无状态会话Bean的生命周期。限于篇幅,本节仅仅讲解无状态会话Bean的生成和消亡。

在ConvertBean.java中增加一个构造函数:

ConvertBean.java

packaSEO靠我ge impl;  import itf.Convert;  import javax.ejb.Remote;  import javax.ejb.Stateless;  @Stateless (maSEO靠我ppedName="ConvertBean")  @Remote  public class ConvertBean implements Convert {      public ConvertBSEO靠我ean(){          System.out.println("ConvertBean构造函数");      }      public String getRmb(String usd){SEO靠我          //从数据库查询汇率,此处简化,假如汇率是6.0          double rate = 6.0;          double dblUsd = Double.parseSEO靠我Double(usd);          double dblRmb = dblUsd * rate;          String rmb = String.valueOf(dblRmb);  SEO靠我        return rmb;      }  }

部署,然后调用TestConvert1.java,在服务器端打印的结果为:

反复运行客户端,服务器端构造函数没有调用,说明是同一个EJB对象为所SEO靠我有客户端服务。关于其生命周期,读者可以参考相关文档。

17.4 有状态会话Bean开发

如前所述,有状态会话Bean,可以存储用户相关信息,在服务器端,一个Bean对象只为客户服务,本节编写有状态会话BeSEO靠我an。

编写有状态会话Bean很简单,以上节的ConvertBean.java为例,只需将代码中的"Stateless"改为"Stateful"即可。代码为:

ConvertBean.java

packagSEO靠我e impl;  import itf.Convert;  import javax.ejb.Remote;  import javax.ejb.Stateful;  @Stateful (mappeSEO靠我dName="ConvertBean")  @Remote  public class ConvertBean implements Convert {      public ConvertBeanSEO靠我(){          System.out.println("ConvertBean构造函数");      }      public String getRmb(String usd){   SEO靠我       //从数据库查询汇率,此处简化,假如汇率是6.0          double rate = 6.0;          double dblUsd = Double.parseDouSEO靠我ble(usd);          double dblRmb = dblUsd * rate;          String rmb = String.valueOf(dblRmb);     SEO靠我     return rmb;      }  }

其中,

@ Stateful (mappedName="ConvertBean")  @Remote 

表示该EJB是一个具有远程接口的有状态会话BeanSEO靠我

部署,然后调用TestConvert1.java,在服务器端打印的结果为:

反复运行客户端,服务器端构造函数都有调用,效果如图17-22所示:

图17-22 显示效果

说明是一个EJB对象为相应客户端服务SEO靠我。不过,读者可能会提出一个问题:既然是一个EJB为一个客户服务,是否会出现大量的EJB对象消耗内存的情况呢?实际上,EJB中的"钝化"机制,会让长期不用的EJB对象,过了一段时间从内存中腾出空间,存入SEO靠我缓存。这是EJB的一个特性,读者可以参考相应文献。

另外,客户也可以手工让有状态会话Bean从实例池中删除。方法是:在远程接口和实现类中定义一个方法,并在实现类中为其注释为"@Remove":

ConveSEO靠我rt.java

……  public interface Convert {      ……  public void remove();  }  ConvertBean.java  ……  publiSEO靠我c class ConvertBean implements Convert {      ……      @Remove      public void remove(){         //释SEO靠我放资源  }  }

此后,客户端通过接口调用remove方法即可。

17.5 有配置文件的EJB

观察前面的代码,我们将JNDI名称写在了源代码中:

ConvertBean.java

……  @StatefulSEO靠我 (mappedName="ConvertBean")  @Remote  public class ConvertBean implements Convert {      ……  }

实际上,将该SEO靠我名称写在源代码中,并不是一个好的办法。由于JNDI名称对于各个厂商具有不同的写法,因此,最好的方法是将JNDI名称写在配置文件中。

首先将"@Stateful (mappedName="ConvertBSEO靠我ean")"改为"@Stateful"。编写配置文件的方法如下:

1. 在项目的META-INF下新建ejb-jar.xml,结构如图17-23所示:

图17-23 项目结构

2. 编写ejb-jar.xmSEO靠我l,源代码为:

ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?> <ejb-jar>   <enterprise-beans>     <sessionSEO靠我>       <ejb-name>ConvertBean</ejb-name>       <mapped-name>ConvertBean</mapped-name>     </session>SEO靠我   </enterprise-beans> </ejb-jar>

注意,文件中的"<ejb-name>ConvertBean</ejb-name>"中的"ConvertBean",默认和实现类的名称相SEO靠我同。

编写完毕,部署,同样也可以进行访问。

17.6 编写具有本地接口的EJB

上一节讲解的是含有远程接口的EJB,该EJB可以被远程调用。前面讲过,EJB的设计,不仅仅是为了提供远程调用功能,有时候,在同SEO靠我一个虚拟机内,将EJB实现类的功能用接口形式公布,也可以起到降低耦合性的作用。此时,该接口适合定义为本地(Local)接口。很明显,本地接口的调用比远程接口的调用,资源消耗应该少一些。

将本例中的EJBSEO靠我改为本地接口版本非常简单,只需要在Bean的实现类内进行改变即可,代码如下:

ConvertBean.java

package impl;  import itf.Convert;  import javSEO靠我ax.ejb.Local;  import javax.ejb.Stateless;  @Stateless  @Local  public class ConvertBean implements SEO靠我Convert {      public String getRmb(String usd){          //从数据库查询汇率,此处简化,假如汇率是6.0          double rSEO靠我ate = 6.0;          double dblUsd = Double.parseDouble(usd);          double dblRmb = dblUsd * rate;SEO靠我          String rmb = String.valueOf(dblRmb);          return rmb;      }  }

其中,

@Stateless  @Local 

表示SEO靠我该EJB是一个具有本地接口的无状态会话Bean。

重新部署,我们发现,原先的TestConvert1程序将无法调用该EJB。

实际上,想要访问实现本地接口的EJB,必须让客户端和服务器运行在同一个容器中。SEO靠我比如,在同一个EJB容器中,被另一个EJB访问。或者,在同一个项目中,被JSP或者Servlet访问,等等。和"远程调用"相比,本地调用性能更好,但是失去了远程调用的功能。具体实现,读者可以参考相应资SEO靠我料。

“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2