1 RMI工作原理
2 websphere实现
3 weblogic实现
4 理解体会(x)
1QRMI工作原理
RMI的本质就是实现在不同JVM之间的调?它的实现Ҏ(gu)是在两个JVM中各开一个Stub和SkeletonQ二者通过socket通信来实现参数和q回值的传递?
有关RMI的例子代码网上可以找C,但绝大部分都是通过extend the interface java.rmi.Remote实现Q已l封装的很完善了(jin)Q不免人有N看花的感觉。下面的例子是我在《Enterprise JavaBeans》里看到的,虽然很粗p,但很直观Q利于很快了(jin)解它的工作原理?
1. 定义一个Person的接口,其中有两个business method, getAge() 和getName()
代码:
public interface Person {
public int getAge() throws Throwable;
public String getName() throws Throwable;
}
2. Person的实现PersonServerc?
代码:
public class PersonServer implements Person {
int age;
String name;
public PersonServer(String name, int age) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}
3. 好,我们现在要在Client机器上调用getAge()和getName()q两个business
methodQ那么就得编写相应的Stub(Client?和Skeleton(Server?E序。这是Stub的实玎ͼ(x)
代码:
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class Person_Stub implements Person {
Socket socket;
public Person_Stub() throws Throwable {
// connect to skeleton
socket = new Socket("computer_name", 9000);
}
public int getAge() throws Throwable {
// pass method name to skeleton
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("age");
outStream.flush();
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
return inStream.readInt();
}
public String getName() throws Throwable {
// pass method name to skeleton
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("name");
outStream.flush();
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
return (String)inStream.readObject();
}
}
注意QPerson_Stub和PersonServer一P都implements
Person。它们都实现?jin)getAge()和getName()两个business
methodQ不同的是PersonServer是真的实玎ͼPerson_Stub是徏立socketq接Qƈ向Skeleton发请求,然后通过
Skeleton调用PersonServer的方法,最后接收返回的l果?
4. Skeleton实现
代码:
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.net.ServerSocket;
public class Person_Skeleton extends Thread {
PersonServer myServer;
public Person_Skeleton(PersonServer server) {
// get reference of object server
this.myServer = server;
}
public void run() {
try {
// new socket at port 9000
ServerSocket serverSocket = new ServerSocket(9000);
// accept stub's request
Socket socket = serverSocket.accept();
while (socket != null) {
// get stub's request
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
String method = (String)inStream.readObject();
// check method name
if (method.equals("age")) {
// execute object server's business method
int age = myServer.getAge();
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
// return result to stub
outStream.writeInt(age);
outStream.flush();
}
if(method.equals("name")) {
// execute object server's business method
String name = myServer.getName();
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
// return result to stub
outStream.writeObject(name);
outStream.flush();
}
}
} catch(Throwable t) {
t.printStackTrace();
System.exit(0);
}
}
public static void main(String args []) {
// new object server
PersonServer person = new PersonServer("Richard", 34);
Person_Skeleton skel = new Person_Skeleton(person);
skel.start();
}
}
Skeletonc?extends from ThreadQ它镉K在后台运行,随时接收client发过来的request。ƈҎ(gu)发送过来的
key去调用相应的business method?
5. 最后一个,Client的实?
代码:
public class PersonClient {
public static void main(String [] args) {
try {
Person person = new Person_Stub();
int age = person.getAge();
String name = person.getName();
System.out.println(name + " is " + age + " years old");
} catch(Throwable t) {
t.printStackTrace();
}
}
}
Client的本质是Q它要知道P(yng)erson接口的定义,q实例一个Person_StubQ通过Stub来调用business methodQ?/p>
至于Stub怎么dServer沟通,Client׃用管?jin)?
注意它的写法Q?
Person person = new Person_Stub();
而不?
Person_Stub person = new Person_Stub();
Z么?因ؓ(f)要面向接口编E嘛Q呵c(din)?/p>
//RMI实质上就是生?个类stubQskeleton来进行参数和q回值的传递,采用g递方?/font>
//cM于以前写的聊天室E序Q被传递的对象应实现java.io.Serializable接口
2Qwebsphere实现
EJBcM?nbsp;
q里l合WebSphere来讲讲各个类的调用关pd?
假定我们要创Z个读取User信息的SessionBeanQ需要我们写的有3个文Ӟ(x)
1. UserServiceHome.java
Home接口
2. UserService.java
Remote接口
3. UserServiceBean.java
Bean实现
WSAD最l会(x)生成10个class。其?个是什么呢Q我们一个一个数q来Q?
4. _UserServiceHome_Stub.java
q个当然是Home接口在Client?动态加?的StubcM(jin)Q它implements UserServiceHome?
5. _EJSRemoteStatelessUserServiceHome_a940aa04_Tie.java
Home接口在Server端的Skeletonc,"a940aa04"应该是随机生成的Q所有其他的相关class名里都会(x)有这个标?/p>
ԌTie是Corba对Skeleton的叫法?
6. EJSRemoteStatelessUserServiceHome_a940aa04.java
Home接口在Server端的实现Q当?dng)它也implements UserServiceHome?
7. EJSStatelessUserServiceHomeBean_a940aa04.java
?6调用Qcreate _UserService_Stub?Z?6不能直接create _UserService_Stub呢?后面再讲?
8. _UserService_Stub.java
Remote接口在Client?动态加?的StubcR它implements UserService?
9. _EJSRemoteStatelessUserService_a940aa04_Tie.java
Remote接口在Server端的SkeletoncR?
10. EJSRemoteStatelessUserService_a940aa04.java
Remote接口在Server端的实现Q当?dng)它也implements UserService。ƈ且,它负责调用UserServiceBean—?/p>
也就是我们所写的Bean实现cZ—里面的business method?
那么Q各个类之间的调用关pd底是怎么L(fng)呢?单的_(d)是两次RMI循环?
W一个RMI循环
先来看看Client端的E序是怎么写的Q?
代码:
try {
InitialContext ctx = new InitialContext();
//W一?
UserServiceHome home =
(UserServiceHome) PortableRemoteObject.narrow(
ctx.lookup(JNDIString),
UserServiceHome.class);
//home: _UserServiceHome_Stub
System.out.println(home.toString());
//W二?
UserService object = home.create();
//ojbect: _UserService_Stub
System.out.println(object.toString());
//W三?
int userId = 1;
UserInfo ui = object.getUserInfo(userId);
}
在第一步之后,我们得到?jin)一个UserServiceHome(interface)定义的对象homeQ那么,home到底是哪个class?/p>
instance呢?用debug看一下,知道?jin)home原来是_UserServiceHome_Stub的实例?
从第二步开始,是我们的关注所在,虽然只有单的一行代码,
UserService object = home.create();
但是他背后的pȝ是怎么q做的呢Q我们进入代码来看吧Q?
1. 调用home.create()
代码:
UserServiceHome home;
UserService obj = home.create();
2. 实际是调用_UserServiceHome_Stub.create()Q在q个Ҏ(gu)里面QStub向Skeleton发送了(jin)一个create的字?/p>
Q?
代码:
org.omg.CORBA.portable.OutputStream out = _request("create", true);
in = (org.omg.CORBA_2_3.portable.InputStream)_invoke(out);
3. Server端的Skeleton接收Stub发来的requestQƈ调用相应的方法:(x)
代码:
_EJSRemoteStatelessUserServiceHome_a940aa04_Tie._invoke() {
......
switch (method.length()) {
case 6:
if (method.equals("create")) {
return create(in, reply);
}
......
}
}
代码:
_EJSRemoteStatelessUserServiceHome_a940aa04_Tie.create() {
EJSRemoteStatelessUserServiceHome_a940aa04 target = null;
result = target.create();
org.omg.CORBA.portable.OutputStream out = reply.createReply();
Util.writeRemoteObject(out,result);
return out;
}
4. Skeleton调用的是UserServiceHome的Server端实现类的createҎ(gu)
代码:
EJSRemoteStatelessUserServiceHome_a940aa04.create() {
UserService _EJS_result;
_EJS_result = EJSStatelessUserServiceHomeBean_a940aa04.create();
}
5. #4又调用EJSStatelessUserServiceHomeBean_a940aa04.create()
代码:
UserService result = super.createWrapper(new BeanId(this, null));
xQ我们终于结束了(jin)W一个RMI循环Qƈ得到?jin)Remote接口UserService的Stubc_UserService_StubQ就?5
里面的result?
q里有一个问题,Z?4不直接create _UserService_StubQ而又转了(jin)一?5的手呢?因ؓ(f)#4 extends from
EJSWrapperQ它没有能力create StubQ因此必d助#5Qwhich extends from EJSHomeQ这h可以生成一?/p>
Stub。如果不是ؓ(f)?jin)生成这个StubQ应该可以不?5q一步?
W二个RMI循环
OK, now we got the object which is instanceOf _UserService_Stub, and implements UserService
现在我们的Client端走到第三步?jin)?x)
UserInfo ui = object.getUserInfo(userId);
l箋看代码,开始第二个RMI循环Q?
1. 调用object.getUserInfo()
代码:
UserService object;
object.getUserInfo(userId);
2. 实际是调用_UserService_Stub.getUserInfo(int
arg0)Q在q个Ҏ(gu)里面QStub向Skeleton发送了(jin)一个getUserInfo的字串和arg0q个参数Q?
代码:
org.omg.CORBA.portable.OutputStream out = _request("getUserInfo", true);
out.write_long(arg0);
in = (org.omg.CORBA_2_3.portable.InputStream)_invoke(out);
3. Server端的Skeleton接收Stub发来的requestQƈ调用相应的方法:(x)
代码:
_EJSRemoteStatelessUserService_a940aa04_Tie._invoke() {
switch (method.charAt(5))
{
case 83:
if (method.equals("getUserInfo")) {
return getUserInfo(in, reply);
}
......
}
}
_EJSRemoteStatelessUserService_a940aa04_Tie.getUserInfo() {
EJSRemoteStatelessUserService_a940aa04 target = null;
int arg0 = in.read_long();
UserDTO result = target.getUserInfo(arg0);
org.omg.CORBA_2_3.portable.OutputStream out = reply.createReply();
out.write_value(result,UserDTO.class);
return out;
}
4. Skeleton调用的是UserService的Server端实现类的getUserInfoҎ(gu)
代码:
EJSRemoteStatelessUserService_a940aa04.getUserInfo() {
UserServiceBean _EJS_beanRef = container.preInvoke(this, 0, _EJS_s);
_EJS_result = _EJS_beanRef.getUserInfo(id);
}
最后的最后,#4l于调用?jin)我们写的UserServiceBean里的getUserInfoҎ(gu)Q这才是我们真正惌d的事?/p>
?
xQ第二个RMI循环也终于结束了(jin)?
调用程?nbsp;
回顾一下上面的分析Q可以很清晰的看CơRMI循环的过E,下图(见链?描述?jin)整个流E:(x)
http://www.pbase.com/image/27229257
黄色?Q?Q?0是程序员要写的,其余是系l生成的?
#1是Home interface, #2?4都implements ?jin)它?
#6是Remote interface, #7?9都implements ?jin)它?
#10是Bean实现?
3Qweblogic实现
一个远E对象至要包括4个class文gQ远E对象;q程对象的接口;实现q程接口的对象的stubQ对象的ske
letonq?个class文g?/p>
在EJB中则臛_要包?0个classQ?/p>
Beanc,特定App Server的Bean实现c?/p>
Bean的remote接口Q特定App Server的remote接口实现c,特定App
Server的remote接口的实现类的stubcdskeletonc?/p>
Bean的home接口Q特定App Server的home接口实现c,特定App
Server的home接口的实现类的stubcdskeletonc?/p>
和RMI不同的是QEJB中这10个class真正需要用L(fng)写的只有3个,分别是Beancd它的remote接口Qhome接口
Q至于其它的7个class到底是怎么生成Q被打包在什么地方,或者是否需要更多的cLӞ?x)根据不同的App
Server表现出比较大的差异,不能一概而论?/p>
拿Weblogic的来说吧QW(xu)eblogic的Bean实现c,以及(qing)两个接口的Weblogic的实现类是在ejbc的时候被打包到EJB
的jar包里面的Q这3个class文g可以看到。而home接口和remote接口的Weblogic的实现类的stubcdskeleton
cL在EJB被部|到Weblogic的时候,由Weblogic动态生成stubcdSkeletoncȝ字节码,因此看不到这4个类
文g?/p>
对于一ơ客L(fng)q程调用EJBQ要l过两个q程对象的多ơRMI循环。首先是通过JNDI查找Home接口Q获得Home
接口的实现类Q这个过E其实相当复杂,首先是找到Home接口的Weblogic实现c,然后创徏一个Home接口的Web
logic实现cȝstubcȝ对象实例Q将它序列化传送给客户端(注意stubcȝ实例是在W?ơRMI循环中,由服?/p>
器动态发送给客户端的Q因此不需要客L(fng)保存Home接口的Weblogic实现cȝstubc)(j)Q最后客L(fng)获得该stu
bcȝ对象实例Q普通的RMI需要在客户端保存stubc,而EJB不需要,因ؓ(f)服务器会(x)把stubcȝ对象实例发送给
客户端)(j)?/p>
客户端拿到服务器l它的Home接口的Weblogic实现cȝstubcd象实例以后,调用stubcȝcreateҎ(gu)Q?在代
码上是home.create()Q但是后台要做很多事?,于是l过W?ơRMI循环Q在服务器端QHome接口的Weblogic
实现cȝskeletoncL到stubcȝ调用信息后,由它再去调用Home接口的Weblogic实现cȝcreateҎ(gu)?/p>
在服务端QHome接口的Weblogic实现cȝcreateҎ(gu)再去调用BeancȝWeblogic实现cȝejbCreateҎ(gu)Q在?/p>
务端创徏或者分配一个EJB实例Q然后将q个EJB实例的远E接口的Weblogic实现cȝstubcd象实例序列化?/p>
送给客户端?/p>
客户端收到remote接口的Weblogic实现cȝstubcȝ对象实例Q对该对象实例的Ҏ(gu)调用Q在客户端代码中?/p>
际上是对remote接口的调用)(j)Q将传送给服务器端remote接口的Weblogic实现cȝskeletoncd象,而skele
toncd象再调用相应的remote接口的Weblogic实现c,然后remote接口的Weblogic实现cd去调用BeancȝWe
blogic实现c,如此完成一ơEJB对象的远E调用?/p>
看了(jin)一遍帖子,感觉q是没有说太清楚Q既然写?jin)帖子,想d把它说清楚?/p>
先拿普通RMI来说Q有4个classQ分别是q程对象Q对象的接口Q对象的stubcdskeletoncR而对象本w和?/p>
象的stubcd旉实现?jin)接口类。而我们在客户端代码调用远E对象的时候,虽然在代码中操纵接口Q实质上
是在操纵stubc,例如Q?/p>
接口c:(x)Hello
q程对象QHello_Server
stubc:(x)Hello_Stub
skeletonc:(x)Hello_Skeleton
客户端代码要q样写:(x)
Hello h = new Hello_Stub();
h.getString();
我们不会(x)q样写:(x)
Hello_Stub h = new Hello_Stub();
h.getString();
因ؓ(f)使用接口适用性更q,q更换?jin)接口实现类Q也不需要更改代码。因此客L(fng)需要Hello.class和Hello_
Stub.classq两个文件。但是对于EJB来说Q就不需要Hello_Stub.classQ因为服务器?x)发送给它,但是Hello.
class文g客户端是省不?jin)的Q必L。表面上我们的客L(fng)代码在操UHelloQ但别忘C(jin)Hello只是一个接?/p>
Q抽象的Q实质上是在操纵Hello_Stub?/p>
拿Weblogic上的EJB举例子,10个class分别是:(x)
Beanc:(x)HelloBean Q用L(fng)写)(j)
BeancȝWeblogic实现c:(x)HelloBean_Impl QEJBC生成Q?br>
Home接口QHelloHome Q用L(fng)写)(j)
Home接口的Weblogic实现c?((Hello Bean))_HomeImplQEJBC生成Q?br>
Home接口的Weblogic实现cȝstubc?((Hello Bean))_HomeImpl_WLStubQ部|的时候动态生成字节码Q?br>
Home接口的Weblogic实现cȝskeletonc?((Hello Bean))_HomeImpl_WLSkeletonQ部|的时候动态生成字节码
Q?br>
Remote接口Q?Hello Q用L(fng)写)(j)
Remote接口的Weblogic实现c?((Hello Bean))_EOImplQEJBC生成Q?br>
Remote接口的Weblogic实现cȝstubc?((Hello Bean))_EOImpl_WLStubQ部|的时候动态生成字节码Q?br>
Remote接口的Weblogic实现cȝskeletonc?((Hello Bean))_EOImpl_WLSkeletonQ部|的时候动态生成字节码
Q?/p>
客户端只需要Hello.class和HelloHome.classq两个文件?/p>
((Hello Home)) home = (Home) ((Portable Remote Object)).narrow(ctx.lookup("Hello"), ((Hello
Home)).class);
q一行代码是从JNDI获得Home接口Q但是请CQ接口是抽象的,那么homeq个对象到底是什么类的对象实?/p>
呢?很简单,用toString()输出看一下就明白?jin),下面一行是输出l果Q?/p>
((Hello Bean))_HomeImpl_WLStub@18c458
q表明homeq个通过从服务器的JNDI?wi)上查找获得的对象实际上是HelloBean_HomeImpl_WLStubcȝ一个实例?/p>
接下来客L(fng)代码Q?/p>
Hello h = home.create()
同样Hello只是一个抽象的接口Q那么h对象是什么东西呢Q打C下:(x)
((Hello Bean))_EOImpl_WLStub@8fa0d1
原来是HelloBean_EOImpl_WLStub的一个对象实例?/p>
用这个例子来qC遍EJB调用q程Q?/p>
首先客户端JNDI查询Q服务端JNDI?wi)上Helloq个名字实际上绑定的对象是HelloBean_HomeImpl_WLStubQ所?/p>
服务端将创徏HelloBean_HomeImpl_WLStub的一个对象实例,序列化返回给客户端?/p>
于是客户端得到home对象Q表面上是得到HelloHome接口的实例,实际上是q行?jin)一ơ远E调用得C(jin)HelloBea
n_HomeImpl_WLStubcȝ对象实例Q别忘记?jin)HelloBean_HomeImpl_WLStub也实C(jin)HelloHome接口?/p>
然后home.create()实质上就是HelloBean_HomeImpl_WLStub.create()Q该Ҏ(gu)发送信息给HelloBean_HomeIm
pl_WLSkeletonQ而HelloBean_HomeImpl_WLSkeleton接受C息后Q再去调用HelloBean_HomeImpl的create?/p>
法,x完成W?ơ完整的RMI循环?/p>
注意在这ơRMI循环q程中,q程对象是HelloBean_HomeImplQ远E对象的接口是HelloHomeQ对象的stub是Hel
loBean_HomeImpl_WLStubQ对象的skeleton是HelloBean_HomeImpl_WLSkeleton?/p>
然后HelloBean_HomeImpl再去调用HelloBean_Impl的ejbCreateҎ(gu)Q而HelloBean_Impl的ejbCreateҎ(gu)负
责创建或者分配一个Bean实例Qƈ且创Z个HelloBean_EOImpl_WLStub的对象实例?/p>
q一步比较有的是,在前一步RMI循环中,q程对象HelloBean_HomeImpl在客L(fng)有一个代理类HelloBean_Ho
meImpl_WLStubQ但在这一步,HelloBean_HomeImpl自己却充当了(jin)HelloBean_Impl的代理类Q只不过HelloBean_
HomeImpl不在客户端,而是在服务端Q因此不q行RMI?/p>
然后HelloBean_EOImpl_WLStub的对象实例序列化q回l客L(fng)Q这一步也很有,上次RMIq程Q主角是Hello
Bean_HomeImpl和它的代理类HelloBean_HomeImpl_WLStubQ但q这一ơ换成了(jin)HelloBean_EOImpl和它的代理类H
elloBean_EOImpl_WLStub来玩?jin)?/p>
Hello h = home.create();h.helloWorld();
假设Hello接口有一个helloWorldq程Ҏ(gu)Q那么表面上是在调用Hello接口的helloWorldҎ(gu)Q实际上是在?/p>
用HelloBean_EOImpl_WLStub的helloWorldҎ(gu)?/p>
然后HelloBean_EOImpl_WLStub的helloWorldҎ(gu)发送信息给服务器上的HelloBean_EOImpl_WLSkeletonQ而H
elloBean_EOImpl_WLSkeleton收到信息以后Q再去调用HelloBean_EOImpl的helloWorldҎ(gu)。至此,完成W??/p>
完整的RMI循环q程?/p>
在刚才HelloBean_EOImpl是作E对象被调用的,它的代理cLHelloBean_EOImpl_WLStubQ但现在HelloBea
n_EOImpl要作为HelloBean_Impl的代理类?jin)。现在HelloBean_EOImpl去调用HelloBean_Impl的helloWorldҎ(gu)
。注意!HelloBean_Impll承?jin)HelloBeanQ而HelloBean中的helloWorldҎ(gu)是我们亲自编写的代码Q现在终
于调用到?jin)我们编写的代码了(jin)?/p>
xQ一ơEJB调用q程l于完成。在整个q程中,服务端主要要调用的类是HelloBean_ImplQ?Hello
Bean?_HomeImplQHelloBean_HomeImpl_WLSkeletonQHelloBean_EOImplQHelloBean_EOImpl_WLSkeleton。客
L(fng)主要调用的类是HelloBean_HomeImpl_WLStubQHelloBean_EOImpl_WLStubQ这两个cd客户端代码中q不
?x)直接出玎ͼ出现在代码中的类是他们的接口HelloHome和HelloQ因此客L(fng)需要这两个接口文gQ而Stub?/p>
服务器传送给他们的?/p>
4 理解体会(x)
单讲Q就是ؓ(f)?jin)适应分布式开发的需要?
首先Q回到我最后给出的程图?
http://www.pbase.com/nobo123/image/27229257
Client端最原始的冲动,肯定是能直接调用#10.UserServiceBeanq?jin)。那么第一个问题来?jin)?
Client和Server不在一个JVM里?
q好办,我们不是有RMI吗,好,q个问题p么解决了(jin)Q?
1. UserServiceBeanInterface.getUserInfo()
2. UserServiceBeanStub
3. UserServiceBeanSkeleton
4. UserServiceBean
用着用着Q第二个问题来了(jin)Q?
UserServiceBean只有人用Q没人管理,transaction logic, security logic, bean instance pooling logic
q些不得不考虑的问题Q出水面了(jin)?
OKQ我们想到用一个delegateQEJBObjectQ来q行所有这些logic的管理。client和EJBObject打交道,EJBObj
ect调用UserServiceBean?
注意Q这个EJBObject也是一个InterfaceQ?6.UserServiceq个interface正是从它extends而来。ƈ且EJBObje
ct所理的这些logicQ正是AppServer的一部分?
现在的流E变Z(jin)Q?
EJBObject
1. UserService.getUserInfo()
2. UserServiceStub
3. UserServiceSkeleton
4. UserServiceImp
5. UserServiceBean
q已l和整幅N?6, #7, #8, #9, #10一一对应?jin)?
现在能满x们的需求了(jin)吗?不,W三个问题又来了(jin)Q?
既然是分布式开发,那么我当然没理由只用一个Specified ServerQ我可能需要用到好几个不同的ServerQ?/p>
且EJBObject也需要管理呀
OKQؓ(f)?jin)适应你的需要,我们q得加再一个HomeObjectQ首先它来决定用哪个Server(当然Q是׃用JNDI
String讑֮?Q其ơ,它来理EJBObject?
注意Q这个EJBHome也是一个InterfaceQ?1.UserServiceHomeq个interface正是从它extends而来。ƈ且EJBHo
me理EJBObject的logicQ也是AppServer的一部分?
现在的调用次序是
1. EJBHome.create()
2. EJBHomeStub
3. EJBHomeSkeleton
4. EJBHomeImp(EJSWrapper)
5. EJSHome
得到EJBObject
6. UserService.getUserInfo()
7. UserServiceStub
8. UserServiceSkeleton
9. UserServiceImp
10. UserServiceBean
现在已经完全和流E图的调用顺序一致了(jin)?
//EJB的基是RMI IIOP,原理q不是很难,关键是实现v来比较绕Q一个简单的功能要用10个(或更多)(j)cL实现Q但每一个都不是多余的?/strong>
//EJB的这U模式(或说RMIQ完全屏蔽了(jin)底层的网l,q很好的实现?jin)对业务代码的保护?/strong>