Vikings

          EJB 工作原理 (ZT) -相見恨晚

          原帖: xanada ----
          http://www.hibernate.org.cn/viewtopic.php?t=3832&postdays=0&postorder=asc&start=0


          前兩天在這個(gè)版塊的精華區(qū)里翻到了Robbin關(guān)于EJB的調(diào)用原理的分析,受益非淺,但感覺用純文字來表達(dá)效果似乎不夠直觀,而且對RMI的闡述也略嫌少了些。這里我根據(jù)自己的一點(diǎn)體會(huì),在Robbin帖子的基礎(chǔ)上再來說說這個(gè)話題,供大家參考。

          首先,我想先說說RMI的工作原理,因?yàn)镋JB畢竟是基于RMI的嘛。廢話就不多講了,RMI的本質(zhì)就是實(shí)現(xiàn)在不同JVM之間的調(diào)用,工作原理圖如下:



          它的實(shí)現(xiàn)方法就是在兩個(gè)JVM中各開一個(gè)Stub和Skeleton,二者通過socket通信來實(shí)現(xiàn)參數(shù)和返回值的傳遞。

          有關(guān)RMI的例子代碼網(wǎng)上可以找到不少,但絕大部分都是通過extend the interface java.rmi.Remote實(shí)現(xiàn),已經(jīng)封裝的很完善了,不免使人有霧里看花的感覺。下面的例子是我在《Enterprise JavaBeans》里看到的,雖然很粗糙,但很直觀,利于很快了解它的工作原理。

          1. 定義一個(gè)Person的接口,其中有兩個(gè)business method, getAge() 和getName()

          java代碼: 


          public interface Person {
              public int getAge() throws Throwable;
              public String getName() throws Throwable;
          }



          2. Person的實(shí)現(xiàn)PersonServer類
          java代碼: 


          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):
          java代碼: 


          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ā)請求,然后通過Skeleton調(diào)用PersonServer的方法,最后接收返回的結(jié)果。

          4. Skeleton實(shí)現(xiàn)
          java代碼: 


          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,它長駐在后臺(tái)運(yùn)行,隨時(shí)接收client發(fā)過來的request。并根據(jù)發(fā)送過來的key去調(diào)用相應(yīng)的business method。

          5. 最后一個(gè),Client的實(shí)現(xiàn)
          java代碼: 


          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,通過Stub來調(diào)用business method,至于Stub怎么去和Server溝通,Client就不用管了。

          注意它的寫法:
          Person person = new Person_Stub();
          而不是
          Person_Stub person = new Person_Stub();

          為什么?因?yàn)橐嫦蚪涌诰幊搪铮呛恰?

          感謝您有耐心看到這里,關(guān)于RMI,我想說的就這么多了。但是好象還沒寫到EJB,本人就累了個(gè)半死,算了,我還是先去睡覺,明天再往下續(xù)吧。。。

          本人沒有用過Weblogic,這里就結(jié)合WebSphere來講講各個(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ù)過來:

          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對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)系到底是怎么樣的呢?簡單的說,就是兩次RMI循環(huán)。

          先來看看Client端的程序是怎么寫的:

          java代碼: 


          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)定義的對象home,那么,home到底是哪個(gè)class的instance呢?用debug看一下,知道了home原來就是_UserServiceHome_Stub的實(shí)例。

          從第二步開始,就是我們的關(guān)注所在,雖然只有簡單的一行代碼,
          UserService object = home.create();
          但是他背后的系統(tǒng)是怎么運(yùn)做的呢?我們進(jìn)入代碼來看吧:

          1. 調(diào)用home.create()
          java代碼: 


          UserServiceHome home;
          UserService obj = home.create();



          2. 實(shí)際是調(diào)用_UserServiceHome_Stub.create(),在這個(gè)方法里面,Stub向Skeleton發(fā)送了一個(gè)create的字串:
          java代碼: 


          org.omg.CORBA.portable.OutputStream out = _request("create", true);
          in = (org.omg.CORBA_2_3.portable.InputStream)_invoke(out);



          3. Server端的Skeleton接收Stub發(fā)來的request,并調(diào)用相應(yīng)的方法:
          java代碼: 


          _EJSRemoteStatelessUserServiceHome_a940aa04_Tie._invoke() {
              ......
              switch (method.length()) {
                  case 6:
                      if (method.equals("create")) {
                          return create(in, reply);
                      }
                  ......
              }
          }



          java代碼: 


          _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方法
          java代碼: 


          EJSRemoteStatelessUserServiceHome_a940aa04.create() {
              UserService _EJS_result;
              _EJS_result = EJSStatelessUserServiceHomeBean_a940aa04.create();
          }



          5. #4又調(diào)用EJSStatelessUserServiceHomeBean_a940aa04.create()
          java代碼: 


              UserService result = super.createWrapper(new BeanId(this, null));



          至此,我們終于結(jié)束了第一個(gè)RMI循環(huán),并得到了Remote接口UserService的Stub類_UserService_Stub,就是#5里面的result。

          這里有一個(gè)問題,為什么#4不直接create _UserService_Stub,而又轉(zhuǎn)了一道#5的手呢?因?yàn)?4 extends from EJSWrapper,它沒有能力create Stub,因此必須借助#5,which extends from EJSHome,這樣才可以生成一個(gè)Stub。如果不是為了生成這個(gè)Stub,應(yīng)該可以不走#5這一步。

          OK, now we got the object which is instanceOf _UserService_Stub, and implements UserService

          現(xiàn)在我們的Client端走到第三步了:
          UserInfo ui = object.getUserInfo(userId);

          繼續(xù)看代碼,開始第二個(gè)RMI循環(huán):

          1. 調(diào)用object.getUserInfo()
          java代碼: 


          UserService object;
          object.getUserInfo(userId);



          2. 實(shí)際是調(diào)用_UserService_Stub.getUserInfo(int arg0),在這個(gè)方法里面,Stub向Skeleton發(fā)送了一個(gè)getUserInfo的字串和arg0這個(gè)參數(shù):

          java代碼: 


          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ā)來的request,并調(diào)用相應(yīng)的方法:
          java代碼: 


          _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方法
          java代碼: 


          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é)束了。

          回顧一下上面的分析,可以很清晰的看到兩次RMI循環(huán)的過程,下圖(見鏈接)描述了整個(gè)流程:

          http://www.pbase.com/image/27229257 
          http://mk23.image.pbase.com/u42/nobo123/upload/27229257.ejb2.jpg

          黃色的1,6,10是程序員要寫的,其余是系統(tǒng)生成的。

          #1是Home interface, #2和#4都implements 了它。
          #6是Remote interface, #7和#9都implements 了它。
          #10是Bean實(shí)現(xiàn)。

          寫到這里,基本要說的就說完了。這實(shí)在是一項(xiàng)累死人的工作,希望您能稀飯。歡迎補(bǔ)充,歡迎摘錯(cuò)。謝謝,呵呵。


          簡單講,就是為了適應(yīng)分布式開發(fā)的需要。

          首先,回到我最后給出的流程圖。

          Client端最原始的沖動(dòng),肯定是能直接調(diào)用#10.UserServiceBean就爽了。那么第一個(gè)問題來了,
          Client和Server不在一個(gè)JVM里

          這好辦,我們不是有RMI嗎,好,這個(gè)問題就這么解決了:
          1. UserServiceBeanInterface.getUserInfo()
          2. UserServiceBeanStub
          3. UserServiceBeanSkeleton
          4. UserServiceBean

          用著用著,第二個(gè)問題來了,
          UserServiceBean只有人用,沒人管理,transaction logic, security logic, bean instance pooling logic這些不得不考慮的問題浮出水面了

          OK,我們想到用一個(gè)delegate,EJBObject,來進(jìn)行所有這些logic的管理。client和EJBObject打交道,EJBObject調(diào)用UserServiceBean。

          注意,這個(gè)EJBObject也是一個(gè)Interface,#6.UserService這個(gè)interface正是從它extends而來。并且EJBObject所管理的這些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一一對應(yīng)了。

          現(xiàn)在能滿足我們的需求了嗎?不,第三個(gè)問題又來了:
          既然是分布式開發(fā),那么我當(dāng)然沒理由只用一個(gè)Specified Server,我可能需要用到好幾個(gè)不同的Server,而且EJBObject也需要管理呀

          OK,為了適應(yīng)你的需要,我們還得加再一個(gè)HomeObject,首先它來決定用哪個(gè)Server(當(dāng)然,是由你用JNDI String設(shè)定的),其次,它來管理EJBObject。

          注意,這個(gè)EJBHome也是一個(gè)Interface,#1.UserServiceHome這個(gè)interface正是從它extends而來。并且EJBHome管理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的調(diào)用確實(shí)很麻煩,但是搞的這么麻煩,確實(shí)是有搞的麻煩的道理,實(shí)在是不得不為也。

          哎喲,好累啊。希望我把這個(gè)問題說清楚了,您也沒給我繞迷糊。謝謝。

          posted on 2005-05-04 03:04 Vikings 閱讀(307) 評論(0)  編輯  收藏 所屬分類: frame-work

          主站蜘蛛池模板: 色达县| 剑河县| 弥勒县| 个旧市| 克什克腾旗| 凤山县| 宾川县| 竹北市| 贵定县| 永州市| 通河县| 三原县| 廉江市| 积石山| 甘肃省| 苏州市| 丰宁| 锡林浩特市| 旺苍县| 额济纳旗| 贺州市| 日喀则市| 康乐县| 界首市| 眉山市| 宁陕县| 鄂尔多斯市| 荥经县| 油尖旺区| 资溪县| 安阳县| 即墨市| 监利县| 永州市| 永城市| 龙泉市| 清原| 施甸县| 肇东市| 麟游县| 云和县|