【IT168 技术文章】
J2EE客户端简介
J2EE的客户端,简单的说就是所有针对EJB而言都处于客户调用逻辑的组件与程序。因为J2EE结构的复杂性,J2EE客户端也比较多,一般分为以下五种。Stand Alone Client,J2EE Application Client,JSP,Servlets,其它Enterprise JavaBeans(处于客户逻辑的EJB)。
这其中,大家对JSP与Servlets可能是比较熟悉的,因为现在基于J2EE的应用开发大部分是Broswer/Server模式,所以它们也是最常用的J2EE的客户端。而EJB本身起到客户端作用,我们也会经常碰到,比如在Session Bean中调用Entity Bean中的商业方法,那么Session Bean 就是这个Entity Bean的客户端。
谈到J2EE就不能不提到EJB。EJB是J2EE结构的核心,我们在它里面实现商业逻辑,而由实现J2EE结构的服务平台提供商为我们提供J2EE Server、EJB Container、Web Container,从而为我们提供诸如安全控制、事务处理、客户连接、以及数据库访问这些服务。这样通过对整个体系划分出不同的角色(如应用开发者,J2EE服务器提供商等等),让我们这些开发者可以专心于商业逻辑的实现,并能最大限度的实现代码的可复用性。
在EJB1.1规范中有两种EJB,一种是Session Bean另一种是Entity Bean。这两种EJB也是我们最常用到的。不论是Session Bean 还是Entity Bean它们在实现上都是由三部分组成。首先是Remote Home Interface,在这个远程接口中定义的是可由客户端调用的创建、查找(对Entity Bean而言)EJB的方法。然后是Remote Interface,这个远程接口中定义的是可供客户端访问的商业方法。最后是Bean Class,这个类对客户端而言是不可访问的,在这个类中我们要具体实现相应的商业方法,以及一些供EJB Container调用的方法。对Session Bean 与Entity Bean,以及对Entity Bean 中的bmp模式以及cmp模式而言,这个类会有很大的不同。但这不是这篇文章要重点介绍的,你只要记住对J2EE的客户端,能看到的只是Remote Interface与Remote Home Interface这两个接口而已。下面让我们看看客户端是如何具体完成对EJB的访问的。
客户端如何访问EJB
不论是那种J2EE的客户端,它要调用一个EJB的相应商业方法都要经过以下这三步:
1、 通过JNDI定位EJB的Remote Home Interface 创建JNDI名称环境,通过在发布时你给EJB定义的JNDI名称找到该EJB的Remote Home Interface。
2、 创建EJB的实例,得到Remote Interface 调用上一步得到的Remote Home Interface中的create()方法,EJB Container会创建相应EJB的实例。而你得到的就是一个定义了EJB要实现的商业方法的Remote Interface。(EJB Container创建EJB实例的基本过程如下:客户端调用create()方法-'EJB Container实例化相应的EJB Bean Class'EJB Container调用Bean Class中的ejbCreate方法'最后返回给我们的是EJB的Remote Interface)
3、 调用Remote Interface中的商业方法 客户端调用上一步创建的Remote Interface中的商业方法,EJB Container就会调用相应Bean Class实例中的相应方法。
下面让我们通过具体的代码来说明这个过程。首先我们先来创建一个Stateless Session Bean。这个Session Bean实现的功能很简单,在其中的商业方法只有一个sayHello方法,打印一句"Hello World:"。如我上边所介绍的这个EJB共有三个部分,它们的源代码如下所示:
包括create()方法的Remote Home Interface接口:
2 import java.rmi.*;
3 import javax.ejb.*;
4 public interface HelloWorldHome extends EJBHome {
5 public HelloWorld create() throws RemoteException, CreateException;
6 }
定义商业方法的Remote Interface接口:
2 import java.rmi.*;
3 import javax.ejb.*;
4 public interface HelloWorld extends EJBObject {
5 public void sayHello() throws RemoteException;
6 }
实现商业方法的Bean Class :
2
3 import java.rmi.*;
4
5 import javax.ejb.*;
6
7 public class HelloWorldBean implements SessionBean {
8
9 private SessionContext sessionContext;
10
11 public void ejbCreate() {}
12
13 public void ejbRemove() throws RemoteException {}
14
15 public void ejbActivate() throws RemoteException {}
16
17 public void ejbPassivate() throws RemoteException {}
18
19 public void setSessionContext(SessionContext sessionContext) throws
20
21 RemoteException {
22
23 this.sessionContext = sessionContext;
24
25 }
26
27 /**唯一的商业方法*/
28
29 public void sayHello(){
30
31 System.out.println("Hello :)");
32
33 }
34
35 }
36
37
有了具体的EJB后,我们就可以看看在J2EE客户端是如何具体完成上边三步的。首先,我们通过JNDI得到HelloWorldHome接口,这个过程又分为以下几步:
创建一个JNDI的名称环境:
Context ctx = new InitialContext();
我这里的例子使用SUN的J2EE SDK1.3发布。在这个发布环境下,创建JNDI的名称环境不需要任何参数,因为这些参数都在系统启动时被自动设置好了。但如果你使用WEBLOGIC或者其它的应用服务器,就需要指定Context.INITIAL_CONTEXT_FACTORY以及Context.PROVIDER_URL参数来创建这个初始的名称环境(有过JNDI的详细情况,可以参考SUN的JNDI 部分)。
通过发布时指定给EJB的JNDI名称,得到我们需要的对象:
Object ref = ctx.lookup("HelloWorld");
这里我给这个EJB起的JNDI名字是HelloWorld。
下面我们要将上一步得到的对象造型成HelloWorldHome对象:
HelloWorldHome helloWorldHome= (HelloWorldHome)PortableRemoteObject.narrow(ref, HelloWorldHome.class);
因为得到的是远程对象,所以这里我们要用javax.rmi.PortableRemoteObject中的方法对我们得到的对象进行造型。
在我们得到了EJB的Remote Home Interface后,我们通过其中的create()方法创建一个EJB实例,并得到Remote Interface。代码如下: HelloWorld helloWorld = helloWorldHome.create();
现在我们就可以通过上一步得到的Remote Interface来调用我们想调用的商业方法。这里我们的EJB只实现了一个sayHello()方法。用如下的代码完成对它的调用: helloWorld.sayHello();
所有的客户端访问EJB都是要经过以上这些步骤。Servlets一般在它的init()方法中完成创建EJB实例的步骤。而JSP访问EJB,最好都通过Java Beans来实现,所以一般在相应Java Bean的初始化方法中完成创建EJB的步骤。当然你也可以根据自己的需要在任意地点完成这些步骤。
这篇文章试图从客户端的角度来向大家介绍J2EE结构,而不是只对客户端进行介绍。所以在这里还有一些其他的信息希望大家能了解一下。在EJB2.0规范中,有一些新的情况出现了,可以对EJB进行本地访问(Local Access)。实现了本地访问的EJB像实现了远程访问的EJB一样有两个接口供客户端访问。一个是Local Interface它像Remote Interface一样定义要实现的商业逻辑。一个是Local Home Interface,它提供创建EJB以及查找(对Entity Bean而言)方法,就像Remote Home Interface一样。这两个Local Interface是供客户端访问而用,而对EJB的实现仍然在Bean Class中。对客户端而言,如果它要访问一个只有本地接口的EJB,那么它必须要和这个EJB在同一个java虚拟机中。而对一个实现远程接口的EJB,当然没有这样的限制。本地访问会提高系统性能,而且在一些场和你必须要让EJB实现本地访问(如container-managed relationship中目标端的Entity Bean,详情请参考EJB2.0规范。一般只是在Entity Bean中实现本地访问接口)。
这样设计一个EJB时你要考虑是实现远程还是本地访问。如果你所有的组件如EJB、WEB、J2EE Client等只是发布于同一台机器上,那么你就可以选择为EJB实现本地接口(当然还有一些必须实现本地接口的情况)。如果你要考虑分布式的情况,那么实现远程访问接口是你唯一的选择。如果你的EJB使用本地访问的话,对客户端就有了一些新的变化。客户端访问实现了本地接口EJB的流程和上面介绍的访问实现远程接口EJB的过程基本一样。但在通过EJB的JNDI名称得到EJB的对象后,对其造型成相应EJB的Local Home接口时,不需要再使用javax.rmi.PortableRemoteObject中的方法,而是直接用相应对象类型造型。假设我们上边的EJB实现的是本地访问接口,那么相应客户端访问代码如下:
2 Object ref = ctx.lookup("HelloWorld");
3 HelloWorldHome helloWorldHome=(HelloWorldHome)ref ;
4
还有在EJB2.0中出现了一种新的EJB:Message-Driven Bean。这种新的EJB类型和其他两种有很大不同。首先,它不像其它两种EJB那样有自己的供客户端调用的Remote或者Local Interface,它只有一个Bean Class。而且客户端也无法定位及调用Message-Driven Bean中的方法。对客户端而言它是不可见的。
下面详细介绍一下J2EE中的两种客户端,Stand alone Client与J2EE Application Client。