1 RMI工作原理
2 websphere實(shí)現(xiàn)
3 weblogic實(shí)現(xiàn)
4 理解體會(huì)
1:RMI工作原理
RMI的本質(zhì)就是實(shí)現(xiàn)在不同JVM之間的調(diào)用,它的實(shí)現(xiàn)方法就是在兩個(gè)JVM中各開(kāi)一個(gè)Stub和Skeleton,二者通過(guò)socket通信來(lái)實(shí)現(xiàn)參數(shù)和返回值的傳遞。
有關(guān)RMI的例子代碼網(wǎng)上可以找到不少,但絕大部分都是通過(guò)extend the interface
java.rmi.Remote實(shí)現(xiàn),已經(jīng)封裝的很完善了,不免使人有霧里看花的感覺(jué)。下面的例子是我在《Enterprise
JavaBeans》里看到的,雖然很粗糙,但很直觀,利于很快了解它的工作原理。
1. 定義一個(gè)Person的接口,其中有兩個(gè)business method, getAge() 和getName()
代碼:
public interface Person {
public int getAge() throws Throwable;
public String getName() throws Throwable;
}
2. Person的實(shí)現(xiàn)PersonServer類
代碼:
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. 好,我們現(xiàn)在要在Client機(jī)器上調(diào)用getAge()和getName()這兩個(gè)business
method,那么就得編寫相應(yīng)的Stub(Client端)和Skeleton(Server端)程序。這是Stub的實(shí)現(xiàn):
代碼:
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();
}
}
注意,Person_Stub和PersonServer一樣,都implements
Person。它們都實(shí)現(xiàn)了getAge()和getName()兩個(gè)business
method,不同的是PersonServer是真的實(shí)現(xiàn),Person_Stub是建立socket連接,并向Skeleton發(fā)請(qǐng)求,然后通過(guò)
Skeleton調(diào)用PersonServer的方法,最后接收返回的結(jié)果。
4. Skeleton實(shí)現(xiàn)
代碼:
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();
}
}
Skeleton類 extends from Thread,它長(zhǎng)駐在后臺(tái)運(yùn)行,隨時(shí)接收client發(fā)過(guò)來(lái)的request。并根據(jù)發(fā)送過(guò)來(lái)的
key去調(diào)用相應(yīng)的business method。
5. 最后一個(gè),Client的實(shí)現(xiàn)
代碼:
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的本質(zhì)是,它要知道Person接口的定義,并實(shí)例一個(gè)Person_Stub,通過(guò)Stub來(lái)調(diào)用business method,
至于Stub怎么去和Server溝通,Client就不用管了。
注意它的寫法:
Person person = new Person_Stub();
而不是
Person_Stub person = new Person_Stub();
為什么?因?yàn)橐嫦蚪涌诰幊搪铮呛恰?/p>
//RMI實(shí)質(zhì)上就是生成2個(gè)類stub,skeleton來(lái)進(jìn)行參數(shù)和返回值的傳遞,采用值傳遞方式
//類似于以前寫的聊天室程序,被傳遞的對(duì)象應(yīng)實(shí)現(xiàn)java.io.Serializable接口
2:websphere實(shí)現(xiàn)
EJB類一覽
這里結(jié)合WebSphere來(lái)講講各個(gè)類的調(diào)用關(guān)系吧。
假定我們要?jiǎng)?chuàng)建一個(gè)讀取User信息的SessionBean,需要我們寫的有3個(gè)文件:
1. UserServiceHome.java
Home接口
2. UserService.java
Remote接口
3. UserServiceBean.java
Bean實(shí)現(xiàn)
WSAD最終會(huì)生成10個(gè)class。其它7個(gè)是什么呢?我們一個(gè)一個(gè)數(shù)過(guò)來(lái):
4. _UserServiceHome_Stub.java
這個(gè)當(dāng)然就是Home接口在Client端(動(dòng)態(tài)加載)的Stub類了,它implements UserServiceHome。
5. _EJSRemoteStatelessUserServiceHome_a940aa04_Tie.java
Home接口在Server端的Skeleton類,"a940aa04"應(yīng)該是隨機(jī)生成的,所有其他的相關(guān)class名里都會(huì)有這個(gè)標(biāo)志
串,Tie是Corba對(duì)Skeleton的叫法。
6. EJSRemoteStatelessUserServiceHome_a940aa04.java
Home接口在Server端的實(shí)現(xiàn),當(dāng)然,它也implements UserServiceHome。
7. EJSStatelessUserServiceHomeBean_a940aa04.java
由#6調(diào)用,create _UserService_Stub。(為什么#6不能直接create _UserService_Stub呢?后面再講。)
8. _UserService_Stub.java
Remote接口在Client端(動(dòng)態(tài)加載)的Stub類。它implements UserService。
9. _EJSRemoteStatelessUserService_a940aa04_Tie.java
Remote接口在Server端的Skeleton類。
10. EJSRemoteStatelessUserService_a940aa04.java
Remote接口在Server端的實(shí)現(xiàn),當(dāng)然,它也implements UserService。并且,它負(fù)責(zé)調(diào)用UserServiceBean——
也就是我們所寫的Bean實(shí)現(xiàn)類——里面的business method。
那么,各個(gè)類之間的調(diào)用關(guān)系到底是怎么樣的呢?簡(jiǎn)單的說(shuō),就是兩次RMI循環(huán)。
第一個(gè)RMI循環(huán)
先來(lái)看看Client端的程序是怎么寫的:
代碼:
try {
InitialContext ctx = new InitialContext();
//第一步
UserServiceHome home =
(UserServiceHome) PortableRemoteObject.narrow(
ctx.lookup(JNDIString),
UserServiceHome.class);
//home: _UserServiceHome_Stub
System.out.println(home.toString());
//第二步
UserService object = home.create();
//ojbect: _UserService_Stub
System.out.println(object.toString());
//第三步
int userId = 1;
UserInfo ui = object.getUserInfo(userId);
}
在第一步之后,我們得到了一個(gè)UserServiceHome(interface)定義的對(duì)象home,那么,home到底是哪個(gè)class的
instance呢?用debug看一下,知道了home原來(lái)就是_UserServiceHome_Stub的實(shí)例。
從第二步開(kāi)始,就是我們的關(guān)注所在,雖然只有簡(jiǎn)單的一行代碼,
UserService object = home.create();
但是他背后的系統(tǒng)是怎么運(yùn)做的呢?我們進(jìn)入代碼來(lái)看吧:
1. 調(diào)用home.create()
代碼:
UserServiceHome home;
UserService obj = home.create();
2. 實(shí)際是調(diào)用_UserServiceHome_Stub.create(),在這個(gè)方法里面,Stub向Skeleton發(fā)送了一個(gè)create的字串
:
代碼:
org.omg.CORBA.portable.OutputStream out = _request("create", true);
in = (org.omg.CORBA_2_3.portable.InputStream)_invoke(out);
3. Server端的Skeleton接收Stub發(fā)來(lái)的request,并調(diào)用相應(yīng)的方法:
代碼:
_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調(diào)用的是UserServiceHome的Server端實(shí)現(xiàn)類的create方法
代碼:
EJSRemoteStatelessUserServiceHome_a940aa04.create() {
UserService _EJS_result;
_EJS_result = EJSStatelessUserServiceHomeBean_a940aa04.create();
}
5. #4又調(diào)用EJSStatelessUserServiceHomeBean_a940aa04.create()
代碼:
UserService result = super.createWrapper(new BeanId(this, null));
至此,我們終于結(jié)束了第一個(gè)RMI循環(huán),并得到了Remote接口UserService的Stub類_UserService_Stub,就是#5
里面的result。
這里有一個(gè)問(wèn)題,為什么#4不直接create _UserService_Stub,而又轉(zhuǎn)了一道#5的手呢?因?yàn)?4 extends from
EJSWrapper,它沒(méi)有能力create Stub,因此必須借助#5,which extends from EJSHome,這樣才可以生成一個(gè)
Stub。如果不是為了生成這個(gè)Stub,應(yīng)該可以不走#5這一步。
第二個(gè)RMI循環(huán)
OK, now we got the object which is instanceOf _UserService_Stub, and implements UserService
現(xiàn)在我們的Client端走到第三步了:
UserInfo ui = object.getUserInfo(userId);
繼續(xù)看代碼,開(kāi)始第二個(gè)RMI循環(huán):
1. 調(diào)用object.getUserInfo()
代碼:
UserService object;
object.getUserInfo(userId);
2. 實(shí)際是調(diào)用_UserService_Stub.getUserInfo(int
arg0),在這個(gè)方法里面,Stub向Skeleton發(fā)送了一個(gè)getUserInfo的字串和arg0這個(gè)參數(shù):
代碼:
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發(fā)來(lái)的request,并調(diào)用相應(yīng)的方法:
代碼:
_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調(diào)用的是UserService的Server端實(shí)現(xiàn)類的getUserInfo方法
代碼:
EJSRemoteStatelessUserService_a940aa04.getUserInfo() {
UserServiceBean _EJS_beanRef = container.preInvoke(this, 0, _EJS_s);
_EJS_result = _EJS_beanRef.getUserInfo(id);
}
最后的最后,#4終于調(diào)用了我們寫的UserServiceBean里的getUserInfo方法,這才是我們真正想要去做的事情
。
至此,第二個(gè)RMI循環(huán)也終于結(jié)束了。
調(diào)用流程圖
回顧一下上面的分析,可以很清晰的看到兩次RMI循環(huán)的過(guò)程,下圖(見(jiàn)鏈接)描述了整個(gè)流程:
http://www.pbase.com/image/27229257
黃色的1,6,10是程序員要寫的,其余是系統(tǒng)生成的。
#1是Home interface, #2和#4都implements 了它。
#6是Remote interface, #7和#9都implements 了它。
#10是Bean實(shí)現(xiàn)。
3:weblogic實(shí)現(xiàn)
一個(gè)遠(yuǎn)程對(duì)象至少要包括4個(gè)class文件:遠(yuǎn)程對(duì)象;遠(yuǎn)程對(duì)象的接口;實(shí)現(xiàn)遠(yuǎn)程接口的對(duì)象的stub;對(duì)象的ske
leton這4個(gè)class文件。
在EJB中則至少要包括10個(gè)class:
Bean類,特定App Server的Bean實(shí)現(xiàn)類
Bean的remote接口,特定App Server的remote接口實(shí)現(xiàn)類,特定App
Server的remote接口的實(shí)現(xiàn)類的stub類和skeleton類
Bean的home接口,特定App Server的home接口實(shí)現(xiàn)類,特定App
Server的home接口的實(shí)現(xiàn)類的stub類和skeleton類
和RMI不同的是,EJB中這10個(gè)class真正需要用戶編寫的只有3個(gè),分別是Bean類和它的remote接口,home接口
,至于其它的7個(gè)class到底是怎么生成,被打包在什么地方,或者是否需要更多的類文件,會(huì)根據(jù)不同的App
Server表現(xiàn)出比較大的差異,不能一概而論。
拿Weblogic的來(lái)說(shuō)吧,Weblogic的Bean實(shí)現(xiàn)類,以及兩個(gè)接口的Weblogic的實(shí)現(xiàn)類是在ejbc的時(shí)候被打包到EJB
的jar包里面的,這3個(gè)class文件可以看到。而home接口和remote接口的Weblogic的實(shí)現(xiàn)類的stub類和skeleton
類是在EJB被部署到Weblogic的時(shí)候,由Weblogic動(dòng)態(tài)生成stub類和Skeleton類的字節(jié)碼,因此看不到這4個(gè)類
文件。
對(duì)于一次客戶端遠(yuǎn)程調(diào)用EJB,要經(jīng)過(guò)兩個(gè)遠(yuǎn)程對(duì)象的多次RMI循環(huán)。首先是通過(guò)JNDI查找Home接口,獲得Home
接口的實(shí)現(xiàn)類,這個(gè)過(guò)程其實(shí)相當(dāng)復(fù)雜,首先是找到Home接口的Weblogic實(shí)現(xiàn)類,然后創(chuàng)建一個(gè)Home接口的Web
logic實(shí)現(xiàn)類的stub類的對(duì)象實(shí)例,將它序列化傳送給客戶端(注意stub類的實(shí)例是在第1次RMI循環(huán)中,由服務(wù)
器動(dòng)態(tài)發(fā)送給客戶端的,因此不需要客戶端保存Home接口的Weblogic實(shí)現(xiàn)類的stub類),最后客戶端獲得該stu
b類的對(duì)象實(shí)例(普通的RMI需要在客戶端保存stub類,而EJB不需要,因?yàn)榉?wù)器會(huì)把stub類的對(duì)象實(shí)例發(fā)送給
客戶端)。
客戶端拿到服務(wù)器給它的Home接口的Weblogic實(shí)現(xiàn)類的stub類對(duì)象實(shí)例以后,調(diào)用stub類的create方法,(在代
碼上就是home.create(),但是后臺(tái)要做很多事情),于是經(jīng)過(guò)第2次RMI循環(huán),在服務(wù)器端,Home接口的Weblogic
實(shí)現(xiàn)類的skeleton類收到stub類的調(diào)用信息后,由它再去調(diào)用Home接口的Weblogic實(shí)現(xiàn)類的create方法。
在服務(wù)端,Home接口的Weblogic實(shí)現(xiàn)類的create方法再去調(diào)用Bean類的Weblogic實(shí)現(xiàn)類的ejbCreate方法,在服
務(wù)端創(chuàng)建或者分配一個(gè)EJB實(shí)例,然后將這個(gè)EJB實(shí)例的遠(yuǎn)程接口的Weblogic實(shí)現(xiàn)類的stub類對(duì)象實(shí)例序列化發(fā)
送給客戶端。
客戶端收到remote接口的Weblogic實(shí)現(xiàn)類的stub類的對(duì)象實(shí)例,對(duì)該對(duì)象實(shí)例的方法調(diào)用(在客戶端代碼中實(shí)
際上就是對(duì)remote接口的調(diào)用),將傳送給服務(wù)器端remote接口的Weblogic實(shí)現(xiàn)類的skeleton類對(duì)象,而skele
ton類對(duì)象再調(diào)用相應(yīng)的remote接口的Weblogic實(shí)現(xiàn)類,然后remote接口的Weblogic實(shí)現(xiàn)類再去調(diào)用Bean類的We
blogic實(shí)現(xiàn)類,如此就完成一次EJB對(duì)象的遠(yuǎn)程調(diào)用。
看了一遍帖子,感覺(jué)還是沒(méi)有說(shuō)太清楚,既然寫了帖子,就想徹底把它說(shuō)清楚。
先拿普通RMI來(lái)說(shuō),有4個(gè)class,分別是遠(yuǎn)程對(duì)象,對(duì)象的接口,對(duì)象的stub類和skeleton類。而對(duì)象本身和對(duì)
象的stub類同時(shí)都實(shí)現(xiàn)了接口類。而我們?cè)诳蛻舳舜a調(diào)用遠(yuǎn)程對(duì)象的時(shí)候,雖然在代碼中操縱接口,實(shí)質(zhì)上
是在操縱stub類,例如:
接口類:Hello
遠(yuǎn)程對(duì)象:Hello_Server
stub類:Hello_Stub
skeleton類:Hello_Skeleton
客戶端代碼要這樣寫:
Hello h = new Hello_Stub();
h.getString();
我們不會(huì)這樣寫:
Hello_Stub h = new Hello_Stub();
h.getString();
因?yàn)槭褂媒涌谶m用性更廣,就算更換了接口實(shí)現(xiàn)類,也不需要更改代碼。因此客戶端需要Hello.class和Hello_
Stub.class這兩個(gè)文件。但是對(duì)于EJB來(lái)說(shuō),就不需要Hello_Stub.class,因?yàn)榉?wù)器會(huì)發(fā)送給它,但是Hello.
class文件客戶端是省不了的,必須有。表面上我們的客戶端代碼在操縱Hello,但別忘記了Hello只是一個(gè)接口
,抽象的,實(shí)質(zhì)上是在操縱Hello_Stub。
拿Weblogic上的EJB舉例子,10個(gè)class分別是:
Bean類:HelloBean (用戶編寫)
Bean類的Weblogic實(shí)現(xiàn)類:HelloBean_Impl (EJBC生成)
Home接口:HelloHome (用戶編寫)
Home接口的Weblogic實(shí)現(xiàn)類 ((Hello Bean))_HomeImpl(EJBC生成)
Home接口的Weblogic實(shí)現(xiàn)類的stub類 ((Hello Bean))_HomeImpl_WLStub(部署的時(shí)候動(dòng)態(tài)生成字節(jié)碼)
Home接口的Weblogic實(shí)現(xiàn)類的skeleton類 ((Hello Bean))_HomeImpl_WLSkeleton(部署的時(shí)候動(dòng)態(tài)生成字節(jié)碼
)
Remote接口: Hello (用戶編寫)
Remote接口的Weblogic實(shí)現(xiàn)類 ((Hello Bean))_EOImpl(EJBC生成)
Remote接口的Weblogic實(shí)現(xiàn)類的stub類 ((Hello Bean))_EOImpl_WLStub(部署的時(shí)候動(dòng)態(tài)生成字節(jié)碼)
Remote接口的Weblogic實(shí)現(xiàn)類的skeleton類 ((Hello Bean))_EOImpl_WLSkeleton(部署的時(shí)候動(dòng)態(tài)生成字節(jié)碼
)
客戶端只需要Hello.class和HelloHome.class這兩個(gè)文件。
((Hello Home)) home = (Home) ((Portable Remote Object)).narrow(ctx.lookup("Hello"), ((Hello
Home)).class);
這一行代碼是從JNDI獲得Home接口,但是請(qǐng)記住!接口是抽象的,那么home這個(gè)對(duì)象到底是什么類的對(duì)象實(shí)例
呢?很簡(jiǎn)單,用toString()輸出看一下就明白了,下面一行是輸出結(jié)果:
((Hello Bean))_HomeImpl_WLStub@18c458
這表明home這個(gè)通過(guò)從服務(wù)器的JNDI樹(shù)上查找獲得的對(duì)象實(shí)際上是HelloBean_HomeImpl_WLStub類的一個(gè)實(shí)例。
接下來(lái)客戶端代碼:
Hello h = home.create()
同樣Hello只是一個(gè)抽象的接口,那么h對(duì)象是什么東西呢?打印一下:
((Hello Bean))_EOImpl_WLStub@8fa0d1
原來(lái)是HelloBean_EOImpl_WLStub的一個(gè)對(duì)象實(shí)例。
用這個(gè)例子來(lái)簡(jiǎn)述一遍EJB調(diào)用過(guò)程:
首先客戶端JNDI查詢,服務(wù)端JNDI樹(shù)上Hello這個(gè)名字實(shí)際上綁定的對(duì)象是HelloBean_HomeImpl_WLStub,所以
服務(wù)端將創(chuàng)建HelloBean_HomeImpl_WLStub的一個(gè)對(duì)象實(shí)例,序列化返回給客戶端。
于是客戶端得到home對(duì)象,表面上是得到HelloHome接口的實(shí)例,實(shí)際上是進(jìn)行了一次遠(yuǎn)程調(diào)用得到了HelloBea
n_HomeImpl_WLStub類的對(duì)象實(shí)例,別忘記了HelloBean_HomeImpl_WLStub也實(shí)現(xiàn)了HelloHome接口。
然后home.create()實(shí)質(zhì)上就是HelloBean_HomeImpl_WLStub.create(),該方法將發(fā)送信息給HelloBean_HomeIm
pl_WLSkeleton,而HelloBean_HomeImpl_WLSkeleton接受到信息后,再去調(diào)用HelloBean_HomeImpl的create方
法,至此完成第1次完整的RMI循環(huán)。
注意在這次RMI循環(huán)過(guò)程中,遠(yuǎn)程對(duì)象是HelloBean_HomeImpl,遠(yuǎn)程對(duì)象的接口是HelloHome,對(duì)象的stub是Hel
loBean_HomeImpl_WLStub,對(duì)象的skeleton是HelloBean_HomeImpl_WLSkeleton。
然后HelloBean_HomeImpl再去調(diào)用HelloBean_Impl的ejbCreate方法,而HelloBean_Impl的ejbCreate方法將負(fù)
責(zé)創(chuàng)建或者分配一個(gè)Bean實(shí)例,并且創(chuàng)建一個(gè)HelloBean_EOImpl_WLStub的對(duì)象實(shí)例。
這一步比較有趣的是,在前一步RMI循環(huán)中,遠(yuǎn)程對(duì)象HelloBean_HomeImpl在客戶端有一個(gè)代理類HelloBean_Ho
meImpl_WLStub,但在這一步,HelloBean_HomeImpl自己卻充當(dāng)了HelloBean_Impl的代理類,只不過(guò)HelloBean_
HomeImpl不在客戶端,而是在服務(wù)端,因此不進(jìn)行RMI。
然后HelloBean_EOImpl_WLStub的對(duì)象實(shí)例序列化返回給客戶端,這一步也很有趣,上次RMI過(guò)程,主角是Hello
Bean_HomeImpl和它的代理類HelloBean_HomeImpl_WLStub,但這這一次換成了HelloBean_EOImpl和它的代理類H
elloBean_EOImpl_WLStub來(lái)玩了。
Hello h = home.create();h.helloWorld();
假設(shè)Hello接口有一個(gè)helloWorld遠(yuǎn)程方法,那么表面上是在調(diào)用Hello接口的helloWorld方法,實(shí)際上是在調(diào)
用HelloBean_EOImpl_WLStub的helloWorld方法。
然后HelloBean_EOImpl_WLStub的helloWorld方法將發(fā)送信息給服務(wù)器上的HelloBean_EOImpl_WLSkeleton,而H
elloBean_EOImpl_WLSkeleton收到信息以后,再去調(diào)用HelloBean_EOImpl的helloWorld方法。至此,完成第2次
完整的RMI循環(huán)過(guò)程。
在剛才HelloBean_EOImpl是作為遠(yuǎn)程對(duì)象被調(diào)用的,它的代理類是HelloBean_EOImpl_WLStub,但現(xiàn)在HelloBea
n_EOImpl要作為HelloBean_Impl的代理類了。現(xiàn)在HelloBean_EOImpl去調(diào)用HelloBean_Impl的helloWorld方法
。注意!HelloBean_Impl繼承了HelloBean,而HelloBean中的helloWorld方法是我們親自編寫的代碼,現(xiàn)在終
于調(diào)用到了我們編寫的代碼了!
至此,一次EJB調(diào)用過(guò)程終于完成。在整個(gè)過(guò)程中,服務(wù)端主要要調(diào)用的類是HelloBean_Impl, Hello
Bean?_HomeImpl,HelloBean_HomeImpl_WLSkeleton,HelloBean_EOImpl,HelloBean_EOImpl_WLSkeleton。客
戶端主要調(diào)用的類是HelloBean_HomeImpl_WLStub,HelloBean_EOImpl_WLStub,這兩個(gè)類在客戶端代碼中并不
會(huì)直接出現(xiàn),出現(xiàn)在代碼中的類是他們的接口HelloHome和Hello,因此客戶端需要這兩個(gè)接口文件,而Stub是
服務(wù)器傳送給他們的。
4 理解體會(huì)
簡(jiǎn)單講,就是為了適應(yīng)分布式開(kāi)發(fā)的需要。
首先,回到我最后給出的流程圖。
http://www.pbase.com/nobo123/image/27229257
Client端最原始的沖動(dòng),肯定是能直接調(diào)用#10.UserServiceBean就爽了。那么第一個(gè)問(wèn)題來(lái)了,
Client和Server不在一個(gè)JVM里。
這好辦,我們不是有RMI嗎,好,這個(gè)問(wèn)題就這么解決了:
1. UserServiceBeanInterface.getUserInfo()
2. UserServiceBeanStub
3. UserServiceBeanSkeleton
4. UserServiceBean
用著用著,第二個(gè)問(wèn)題來(lái)了,
UserServiceBean只有人用,沒(méi)人管理,transaction logic, security logic, bean instance pooling logic
這些不得不考慮的問(wèn)題浮出水面了。
OK,我們想到用一個(gè)delegate,EJBObject,來(lái)進(jìn)行所有這些logic的管理。client和EJBObject打交道,EJBObj
ect調(diào)用UserServiceBean。
注意,這個(gè)EJBObject也是一個(gè)Interface,#6.UserService這個(gè)interface正是從它extends而來(lái)。并且EJBObje
ct所管理的這些logic,正是AppServer的一部分。
現(xiàn)在的流程變?yōu)榱耍?
EJBObject
1. UserService.getUserInfo()
2. UserServiceStub
3. UserServiceSkeleton
4. UserServiceImp
5. UserServiceBean
這已經(jīng)和整幅圖里的#6, #7, #8, #9, #10一一對(duì)應(yīng)了。
現(xiàn)在能滿足我們的需求了嗎?不,第三個(gè)問(wèn)題又來(lái)了:
既然是分布式開(kāi)發(fā),那么我當(dāng)然沒(méi)理由只用一個(gè)Specified Server,我可能需要用到好幾個(gè)不同的Server,而
且EJBObject也需要管理呀
OK,為了適應(yīng)你的需要,我們還得加再一個(gè)HomeObject,首先它來(lái)決定用哪個(gè)Server(當(dāng)然,是由你用JNDI
String設(shè)定的),其次,它來(lái)管理EJBObject。
注意,這個(gè)EJBHome也是一個(gè)Interface,#1.UserServiceHome這個(gè)interface正是從它extends而來(lái)。并且EJBHo
me管理EJBObject的logic,也是AppServer的一部分。
現(xiàn)在的調(diào)用次序是
1. EJBHome.create()
2. EJBHomeStub
3. EJBHomeSkeleton
4. EJBHomeImp(EJSWrapper)
5. EJSHome
得到EJBObject
6. UserService.getUserInfo()
7. UserServiceStub
8. UserServiceSkeleton
9. UserServiceImp
10. UserServiceBean
現(xiàn)在已經(jīng)完全和流程圖的調(diào)用順序一致了。
//EJB的基礎(chǔ)是RMI IIOP,原理并不是很難,關(guān)鍵是實(shí)現(xiàn)起來(lái)比較繞,一個(gè)簡(jiǎn)單的功能要用10個(gè)(或更多)類來(lái)實(shí)現(xiàn),但每一個(gè)都不是多余的。
//EJB的這種模式(或說(shuō)RMI)完全屏蔽了底層的網(wǎng)絡(luò),并很好的實(shí)現(xiàn)了對(duì)業(yè)務(wù)代碼的保護(hù)。