J2EE的客户端

来源:百度文库 编辑:神马文学网 时间:2024/03/29 04:32:58
这篇文章试图从J2EE客户端的角度出发来介绍J2EE体系。将介绍J2EE的客户端如何对EJB进行访问。J2EE的客户端分类。最后重点分析J2EE中两种相似的客户端Stand alone Client与J2EE Application Client。并会给出针对同一个Stateless Session Bean的这两种不同客户端的实现实例来具体说明它们的异同。本文的所有实例都基于SUN的J2EE SDK进行发布。附录中也会对SUN的这个非常方便我们学习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的访问的。




回页首
不论是那种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接口:
package com.javausr.example; import java.rmi.*; import javax.ejb.*; public interface HelloWorldHome extends EJBHome { public HelloWorld create() throws RemoteException, CreateException; }
定义商业方法的Remote Interface接口:
package com.javausr.example; import java.rmi.*; import javax.ejb.*; public interface HelloWorld extends EJBObject { public void sayHello() throws RemoteException; }
实现商业方法的Bean Class :
package com.javausr.example; import java.rmi.*; import javax.ejb.*; public class HelloWorldBean implements SessionBean { private SessionContext sessionContext; public void ejbCreate() {} public void ejbRemove() throws RemoteException {} public void ejbActivate() throws RemoteException {} public void ejbPassivate() throws RemoteException {} public void setSessionContext(SessionContext sessionContext) throws RemoteException { this.sessionContext = sessionContext; } /**唯一的商业方法*/ public void sayHello(){ System.out.println("Hello :)"); } }
有了具体的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实现的是本地访问接口,那么相应客户端访问代码如下:
Context ctx = new InitialContext(); Object ref = ctx.lookup("HelloWorld"); HelloWorldHome helloWorldHome=(HelloWorldHome)ref ;
还有在EJB2.0中出现了一种新的EJB:Message-Driven Bean。这种新的EJB类型和其他两种有很大不同。首先,它不像其它两种EJB那样有自己的供客户端调用的Remote或者Local Interface,它只有一个Bean Class。而且客户端也无法定位及调用Message-Driven Bean中的方法。对客户端而言它是不可见的。
下面详细介绍一下J2EE中的两种客户端,Stand alone Client与J2EE Application Client。




回页首
这种客户端就如它的名字一样,它独立于J2EE,不是J2EE的组件。它一般用于测试我们开发的EJB。下面是一个具体的例子,它访问的EJB是上边的那个Stateless Session Bean。
import javax.naming.Context; import javax.naming.InitialContext; import javax.rmi.PortableRemoteObject; import com.javausr.example.HelloWorld; import com.javausr.example.HelloWorldHome; public class MyStandAloneClient { public static void main(String[] args) { try { //创建jndi初始化环境 Context initial = new InitialContext(); //找到ejb的Remote Home Interface Object objref = initial.lookup("HelloWorld"); HelloWorldHome home = (HelloWorldHome)PortableRemoteObject.narrow(objref, HelloWorldHome.class); //初始化ejb,得到Remote Interface HelloWorld helloWorld = home.create(); //调用商业方法sayHello() helloWorld.sayHello(); System.exit(0); } catch (Exception ex) { System.err.println("Caught an unexpected exception!"); ex.printStackTrace(); } } }
打开一个dos窗口,使用如下的指令编译我们的代码(请根据自己的环境对里面的参数作相应调整):
D:\j2sdkee1.3\myapp\HELLOW~1>javac -classpath .;d:\j2sdkee1.3\lib\j2ee.jar MyStandAloneClient.java
编译完成后在运行这个例子之前,要在我们的J2EE 服务器上发布我们所实现的EJB。这里我使用的J2EE服务器是J2EE SDK 1.3,有关它的介绍请见本文的附录1--J2EE SDK1.3介绍。如何在它上边发布HelloWorld EJB的详细步骤请见附录2--发布HelloWorld Staless Session Bean。
在服务器已经启动,EJB已经发布的情况下,让我们来运行一下上边的例子,使用如下的命令运行:
D:\j2sdkee1.3\myapp\HELLOW~1>java -classpath ".;d:\j2sdkee1.3\lib\j2ee.jar;helloworldClient.jar" MyStandAloneClient
这样你就可以在J2EE Server控制台上看到我们的MyStandAloneClient调用sayHello方法所打印出来的"Hello :)"(使用j2ee -verbose启动服务器)。




回页首
它和Stand alone Client很像,也是一个java application,但不同的是这种客户端是J2EE组件的一种。所以要想使用这种客户端,你就要用相应J2EE服务器的应用发布工具将它发布到服务器上。因为它是J2EE的组件,它与Stand alone Client最大不同在于它可以使用J2EE提供的服务。比如它可以由J2EE统一进行安全认证,在你运行J2EE Application Client时系统会自动弹出一个认证窗口,你输入J2EE服务器上许可的用户名和密码后才能运行这个程序,从而访问J2EE Server上的EJB。它给我们开发带来的好处是可想而知的。
还有在J2EE Application Client中,你可以通过为已有的EJB加入JNDI参考来用自己定义的JNDI名称访问EJB。例如在我的例子中是通过下面的代码来得到EJB对象的: Object objref = initial.lookup("java:comp/env/ejb/hai");
大家也许记得在Stand alone Client例子中,我是通过"HelloWorld"JNDI名称得到同样的对象的。而这里却是"ejb/hai"。用不同的JNDI名称来访问同一个EJB。这样做的好处就是你可以随意更改你的EJB的JNDI名称,而J2EE Application Client程序代码不需要有任何更改,只需要修改EJB参考中的对应关系就可以了。这里是源代码:
import javax.naming.Context; import javax.naming.InitialContext; import javax.rmi.PortableRemoteObject; import com.javausr.example.HelloWorld; import com.javausr.example.HelloWorldHome; public class MyJ2EEAPPClient { public static void main(String[] args) { try { //创建jndi初始化环境 Context initial = new InitialContext(); //找到ejb的Remote Home Interface Object objref = initial.lookup("java:comp/env/ejb/hai"); HelloWorldHome home = (HelloWorldHome)PortableRemoteObject.narrow(objref, HelloWorldHome.class); //初始化ejb,得到Remote Interface HelloWorld helloWorld = home.create(); //调用商业方法sayHello() helloWorld.sayHello(); System.exit(0); } catch (Exception ex) { System.err.println("Caught an unexpected exception!"); ex.printStackTrace(); } } }
你会发现在代码上除了上面提到的JNDI名称不同外,没有其他什么不同。但是我们不能像对Stand alone Client那样,编译完后就运行它,而要先创建一个J2EE Application Client组件,并把它加入到我们的应用中。有关这个创建过程请参考文章附录3--创建J2EE Application Client组件。