<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Vikings

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

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


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

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



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

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

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

    java代碼: 


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



    2. Person的實現(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機器上調(diào)用getAge()和getName()這兩個business method,那么就得編寫相應的Stub(Client端)和Skeleton(Server端)程序。這是Stub的實現(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。它們都實現(xiàn)了getAge()和getName()兩個business method,不同的是PersonServer是真的實現(xiàn),Person_Stub是建立socket連接,并向Skeleton發(fā)請求,然后通過Skeleton調(diào)用PersonServer的方法,最后接收返回的結(jié)果。

    4. Skeleton實現(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,它長駐在后臺運行,隨時接收client發(fā)過來的request。并根據(jù)發(fā)送過來的key去調(diào)用相應的business method。

    5. 最后一個,Client的實現(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接口的定義,并實例一個Person_Stub,通過Stub來調(diào)用business method,至于Stub怎么去和Server溝通,Client就不用管了。

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

    為什么?因為要面向接口編程嘛,呵呵。

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

    本人沒有用過Weblogic,這里就結(jié)合WebSphere來講講各個類的調(diào)用關系吧。

    假定我們要創(chuàng)建一個讀取User信息的SessionBean,需要我們寫的有3個文件:
    1. UserServiceHome.java
    Home接口

    2. UserService.java
    Remote接口

    3. UserServiceBean.java
    Bean實現(xiàn)

    WSAD最終會生成10個class。其它7個是什么呢?我們一個一個數(shù)過來:

    4. _UserServiceHome_Stub.java
    這個當然就是Home接口在Client端(動態(tài)加載)的Stub類了,它implements UserServiceHome。

    5. _EJSRemoteStatelessUserServiceHome_a940aa04_Tie.java
    Home接口在Server端的Skeleton類,"a940aa04"應該是隨機生成的,所有其他的相關class名里都會有這個標志串,Tie是Corba對Skeleton的叫法。

    6. EJSRemoteStatelessUserServiceHome_a940aa04.java
    Home接口在Server端的實現(xiàn),當然,它也implements UserServiceHome。

    7. EJSStatelessUserServiceHomeBean_a940aa04.java
    由#6調(diào)用,create _UserService_Stub。(為什么#6不能直接create _UserService_Stub呢?后面再講。)

    8. _UserService_Stub.java
    Remote接口在Client端(動態(tài)加載)的Stub類。它implements UserService。

    9. _EJSRemoteStatelessUserService_a940aa04_Tie.java
    Remote接口在Server端的Skeleton類。

    10. EJSRemoteStatelessUserService_a940aa04.java
    Remote接口在Server端的實現(xiàn),當然,它也implements UserService。并且,它負責調(diào)用UserServiceBean——也就是我們所寫的Bean實現(xiàn)類——里面的business method。

    那么,各個類之間的調(diào)用關系到底是怎么樣的呢?簡單的說,就是兩次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);
    }



    在第一步之后,我們得到了一個UserServiceHome(interface)定義的對象home,那么,home到底是哪個class的instance呢?用debug看一下,知道了home原來就是_UserServiceHome_Stub的實例。

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

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


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



    2. 實際是調(diào)用_UserServiceHome_Stub.create(),在這個方法里面,Stub向Skeleton發(fā)送了一個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)用相應的方法:
    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端實現(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é)束了第一個RMI循環(huán),并得到了Remote接口UserService的Stub類_UserService_Stub,就是#5里面的result。

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

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

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

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

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


    UserService object;
    object.getUserInfo(userId);



    2. 實際是調(diào)用_UserService_Stub.getUserInfo(int arg0),在這個方法里面,Stub向Skeleton發(fā)送了一個getUserInfo的字串和arg0這個參數(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)用相應的方法:
    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端實現(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方法,這才是我們真正想要去做的事情。

    至此,第二個RMI循環(huán)也終于結(jié)束了。

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

    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實現(xiàn)。

    寫到這里,基本要說的就說完了。這實在是一項累死人的工作,希望您能稀飯。歡迎補充,歡迎摘錯。謝謝,呵呵。


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

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

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

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

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

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

    注意,這個EJBObject也是一個Interface,#6.UserService這個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一一對應了。

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

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

    注意,這個EJBHome也是一個Interface,#1.UserServiceHome這個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)用確實很麻煩,但是搞的這么麻煩,確實是有搞的麻煩的道理,實在是不得不為也。

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

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

    主站蜘蛛池模板: 亚洲avav天堂av在线网毛片| 疯狂做受xxxx高潮视频免费| 免费播放美女一级毛片| 国产免费AV片在线观看| 好爽…又高潮了毛片免费看 | 91成人免费在线视频| 亚洲国产精品成人网址天堂| 91情国产l精品国产亚洲区| 国产亚洲精品成人久久网站| 一区二区三区福利视频免费观看| 国产精品免费综合一区视频| 亚洲成人午夜在线| 特黄aa级毛片免费视频播放| 国产免费不卡视频| 亚洲线精品一区二区三区 | 美女被cao免费看在线看网站| AV在线亚洲男人的天堂| 中文字幕乱码亚洲精品一区| a级毛片毛片免费观看永久| 老司机永久免费网站在线观看| 亚洲激情在线观看| 一级特黄a大片免费| 久久久久久国产精品免费免费| 精品国产亚洲一区二区三区| 国产偷国产偷亚洲高清人| 99久久久精品免费观看国产| 亚洲中文字幕无码日韩| 国产天堂亚洲精品| 成年女人午夜毛片免费视频| 亚洲电影免费在线观看| eeuss影院免费直达入口| 在线a毛片免费视频观看| 亚洲精品视频久久| 免费国产成人午夜在线观看| 亚洲AⅤ优女AV综合久久久| 中文有码亚洲制服av片| 67pao强力打造高清免费| 亚洲成A人片在线观看无码不卡| 免费视频成人国产精品网站| 精品国产免费观看久久久| 亚洲免费在线观看视频|