posts - 297,  comments - 1618,  trackbacks - 0
                說明:本文為孫衛琴的《Java網絡編程精解》第10章的學習筆記。

          Java反射機制主要提供了如下功能:

          l         在運行時判斷任何一個對象所屬的類;

          l         在運行時構造任意一個類的對象;

          l         在運行時判斷任何一個類所具有的成員變量和方法;

          l         在運行時調用任何一個對象的方法;

          l         生成動態代理。

          一.             Java Reflection API簡介

          JDK中,主要由以下類來實現Java反射機制,這些類都位于java.lang.reflect包中:

          l         Class類:代表一個類;

          l         Field類:代表類的成員變量;

          l         Method類:代表類的方法;

          l         Constructor:代表類的構造方法;

          l         Array:提供了動態創建數組,以及訪問數組元素的靜態方法。

          至于它們的使用,請參見我先前的一篇文章:Java反射機制學習筆記(一),在此不再贅述。

          二.             在遠程方法調用中運用反射機制

          讓我們來看一個在遠程調用方法中調用運用反射機制的例子。該例的服務端SimpleServer接收客戶端SimpleClient發送的Call對象,該Call類型對象包括要調用的遠程對象的類名、方法名、參數類型和參數值信息。而服務端SimpleServer在接收到該對象時,調用指定類名的指定方法,并加組裝了返回值的Call類型對象返回給客戶端SimpleClient。若不存在所指定的類或方法,則將異常放入Call類型對象的result屬性中,返回給客戶端。下面讓我們來看看這個例子:

          1.       Call對象

          Call對象包含類名、方法名、參數類型、參數值信息和返回結果信息,是用來在客戶端和服務端進行信息交互的對象。其代碼如下:

          package remotecall;

          import java.io.Serializable;
          publicclass Call 
          implements Serializable {
              
          //類名或接口名
              private String className;

              
          //方法名
              private String methodName;

              
          //方法參數類型
              private Class[] paramTypes;

              
          //方法參數值
              private Object[] params;

              
          //返回方法的執行結果,正常執行時,存放返回值,發生異常時,result為該異常
              private Object result;

              
          public Call() {   
              }


              
          /**
               *構造函數.
               
          */

              
          public Call(String className, String methodName,
                     Class[] paramTypes, Object[] params) 
          {
                 
          this.className = className;
                 
          this.methodName = methodName;
                 
          this.paramTypes = paramTypes;
                 
          this.params = params;
              }


              
          //省略className、methodName、paramTypes、params和result的set/get方法
              public String toString() {
                 
          return"className=" + className + ",methodName=" + methodName;
              }

          }

           2.       服務端SimpleServer

          服務端建立一個ServerSocket,一直讀取客戶端發送來的消息,并傳入參數到指定的方法,調用該方法,并將返回結果設置到Call類型對象的result屬性中,若出現異常情況時,將異常放入result屬性中,并將改變后的Call類型對象返回。其代碼如下所示:

          package remotecall;

          import java.io.InputStream;
          import java.io.ObjectInputStream;
          import java.io.ObjectOutputStream;
          import java.io.OutputStream;
          import java.lang.reflect.Method;
          import java.net.ServerSocket;
          import java.net.Socket;
          import java.util.HashMap;
          import java.util.Map;

          /**
           *服務端.
           
          */

          publicclass SimpleServer 
          {
              
          //存放遠程對象的緩存
              private Map<String, Object> remoteObjects = new HashMap<String, Object>();
              
              
          /**
               *將一個遠程對象加入緩存中.
               *@paramclassNamemap中的key——類名
               *@paramremoteObject遠程對象
               
          */

              publicvoid register(String className, Object remoteObject) 
          {
                 remoteObjects.put(className, remoteObject);
              }

              
              publicvoid service() 
          throws Exception {
                 ServerSocket serverSocket 
          = new ServerSocket(8000);
                 System.out.println(
          "服務器啟動");
                 
          while(true{
                     Socket socket 
          = serverSocket.accept();
                     InputStream in 
          = socket.getInputStream();
                     ObjectInputStream ois 
          = new ObjectInputStream(in);
                     OutputStream out 
          = socket.getOutputStream();
                     ObjectOutputStream oos 
          = new ObjectOutputStream(out);

                     
          //接收從客戶端發送的Call對象
                     Call call = (Call) ois.readObject();
                     
          //調用call的toString()方法打出className和methodName
                     System.out.println(call);
                     
          //調用對象的相關方法
                     call = invoke(call);
                     
          //將放置了result值的對象放入輸出中返回
                     oos.writeObject(call);

                     
          //關閉相關資源
                     ois.close();
                     oos.close();
                     socket.close();
                 }

              }


              
          /**
               *調用遠程方法的指定方法,并將返回值放入call對象的result中.
               *@paramcall調用對象
               *@return返回設置了result值的call對象
               
          */

              
          public Call invoke(Call call) {
                 Object result 
          = null;
                 
          try {
                     
          //取出對象中的各參數
                     String className = call.getClassName();
                     String methodName 
          = call.getMethodName();
                     Class[] paramTypes 
          = call.getParamTypes();
                     Object[] params 
          = call.getParams();
                     
                     
          //獲取類
                     Class classType = Class.forName(className);
                     
          //獲取方法
                     Method method = classType.getMethod(methodName, paramTypes);
                     
          //將className作為key在map中取出遠程對象
                     Object remoteObject = remoteObjects.get(className);
                     
          if (remoteObject == null{
                        thrownew Exception(className 
          + "遠程對象不存在!");
                     }
           else {
                        
          //通過傳入相應參數調用remoteObject的指定方法
                        
          //并將返回值放入result中.
                        result = method.invoke(remoteObject, params);
                     }

                 }
           catch(Exception e) {
                     result 
          = e;
                 }


                 
          //設置返回值
                 call.setResult(result);
                 
          return call;
              }


              
          /**
               *測試方法.
               
          */

              publicstaticvoid main(String[] args) 
          throws Exception {
                 SimpleServer server 
          = new SimpleServer();
                 
          //存放對象到remoteObjects這個map中
                 server.register("remotecall.HelloService"new HelloServiceImpl());
                 server.service();
              }

          }

           3.    客戶端SimpleClient

          客戶端發送組裝好的Call對象給服務端,并讀取指定方法的返回結果。其完整代碼如下:

          package remotecall;

          import java.io.InputStream;
          import java.io.ObjectInputStream;
          import java.io.ObjectOutputStream;
          import java.io.OutputStream;
          import java.net.Socket;

          /**
           *客戶端類.
           
          */

          publicclass SimpleClient 
          {
              publicvoid invoke(Call call) 
          throws Exception {
                 Socket socket 
          = new Socket("localhost"8000);
                 OutputStream out 
          = socket.getOutputStream();
                 ObjectOutputStream oos 
          = new ObjectOutputStream(out);
                 InputStream in 
          = socket.getInputStream();
                 ObjectInputStream ois 
          = new ObjectInputStream(in);  

                 
          //向服務器發送call對象
                 oos.writeObject(call);
                 
          //接收從服務端發送回的對象
                 call = (Call) ois.readObject();
                 
          //打印結果信息
                 System.out.println(call.getResult());

                 
          //關閉資源
                 ois.close();
                 oos.close();
                 socket.close();
              }

             
              
          /**
               *測試方法.
               
          */

              publicstaticvoid main(String[] args) 
          throws Exception {
                 SimpleClient client 
          = new SimpleClient();

                 
          //構建一個正確Call對象
                 Call call = new Call();
                 call.setClassName(
          "remotecall.HelloService");
                 call.setMethodName(
          "echo");
                 call.setParamTypes(
          new Class[]{String.class});
                 call.setParams(
          new Object[]{"Hello,阿蜜果"});
                 client.invoke(call);
                 
                 
          //構建一個錯誤的Call對象(不存在所指定的類)
                 call.setClassName("remotecall.HelloEcho");
                 client.invoke(call);
              }

          }

          4.    遠程類HelloService及其實現類HelloServiceImpl

          為了測試上面的功能,還需要模擬一個遠程對象所屬的類,本例的HelloService接口具有兩個方法,echo()getTime()。兩者的內容如下:

          HelloService的內容:

          package remotecall;

          import java.util.Date;

          publicinterface HelloService 
          {
              
          public String echo(String msg);

              
          public Date getTime();
          }

          HelloServiceImpl的內容:

          package remotecall;

          import java.util.Date;

          publicclass HelloServiceImpl 
          implements HelloService {
              
          public String echo(String msg) {
                 
          return"echo: " + msg;
              }


              
          public Date getTime() {
                 returnnew Date();
              }

          }

              在測試時,我們首先運行服務端SimpleServer,將服務端啟動起來,接著將客戶端SimpleClient啟動,可在控制臺看到如下信息:

          客戶端的信息如下:

          echo Hello,阿蜜果

          java.lang.ClassNotFoundException: remotecall.HelloEcho

          服務端的信息如下:

          服務器啟動...

          className=remotecall.HelloServicemethodName=echo

          className=remotecall.HelloEchomethodName=echo

          三.代理模式

          代理模式是常用的Java設計模式,它的特征是代理類和委托類有相同的接口。代理類主要負責為委托類預處理消息、過濾信息、把消息轉發給委托類,以及事后處理信息等。代理類和委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。

          根據代理類的創建時期,可將其分為兩類:

          l         靜態代理類:由程序員創建或由特定工具自動生成源代碼;

          l         動態代理類:在程序運行時,運用反射機制創建而成。

          1.    靜態代理類

          請參考代理模式的一些實現實例,在此不再詳述。

          2.    動態代理類

          動態代理類不僅簡化了編程工作,而且提高了軟件系統的擴展性,因為Java反射機制可以生成任意類型的動態代理類。java.lang.reflect類和InvocationHandler接口提供了生成動態代理類的能力。與之相關的方法是:getProxyClass()newProxyInstance()方法。下面讓我們來看一個動態代理類的簡單例子:

          package proxy;

          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;
          import java.lang.reflect.Proxy;

          /**
           *動態代理類.
           
          */

          publicclass HelloServiceProxyFactory 
          {
              publicstatic HelloService getHelloServiceProxy(
          final HelloService helloService) {
                 InvocationHandler handler 
          = new InvocationHandler() {
                     
          public Object invoke(Object proxy, Method method, Object args[])
                            
          throws Exception {
                        System.out.println(
          "before calling " + method);
                        Object result 
          = method.invoke(helloService, args);
                        System.out.println(
          "after calling " + method);
                        
          return result;
                     }

                 }
          ;
                 
                 Class classType 
          = HelloService.class;
                 
          return (HelloService) Proxy.newProxyInstance(classType.getClassLoader(),
                        
          new Class[]{classType},
                        handler);
              }


              
          /**
               *測試方法.
               
          */

              publicstaticvoid main(String[] args) 
          {
                 HelloService helloService 
          = new HelloServiceImpl();
                 HelloService helloServiceProxy 
          = HelloServiceProxyFactory.getHelloServiceProxy(
                        helloService);
                 System.out.println(
          "代理類名字:" + helloServiceProxy.getClass().getName());
                 System.out.println(helloService.echo(
          "Hello,阿蜜果"));
              }

          }


               運行后可看到這個代理類是動態生成的。在SpringAOP中也運到了動態代理機制,有興趣的朋友可查找相關資料。

          posted on 2007-09-19 13:21 阿蜜果 閱讀(2757) 評論(3)  編輯  收藏 所屬分類: Java


          FeedBack:
          # re: Java反射機制學習筆記(二)
          2007-09-19 14:46 | 千里冰封
          呵呵,不錯,JAVA這方面確實挺強大的,可以在運行時得到有關類的一些信息
            回復  更多評論
            
          # re: Java反射機制學習筆記(二)
          2007-09-19 19:41 |
          文章寫的不錯,人長得很好看
          頂一下  回復  更多評論
            
          # re: Java反射機制學習筆記(二)
          2007-10-12 18:29 | 大媽
          文章寫的不錯,人長得很好看 ……——……  回復  更多評論
            
          <2007年9月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          30123456

                生活將我們磨圓,是為了讓我們滾得更遠——“圓”來如此。
                我的作品:
                玩轉Axure RP  (2015年12月出版)
                

                Power Designer系統分析與建模實戰  (2015年7月出版)
                
               Struts2+Hibernate3+Spring2   (2010年5月出版)
               

          留言簿(263)

          隨筆分類

          隨筆檔案

          文章分類

          相冊

          關注blog

          積分與排名

          • 積分 - 2296322
          • 排名 - 3

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 偏关县| 荣成市| 武隆县| 金寨县| 西盟| 洛宁县| 奎屯市| 江西省| 建湖县| 兴山县| 临桂县| 商城县| 封丘县| 娄底市| 三都| 张家口市| 岱山县| 卢龙县| 宣武区| 中卫市| 贵定县| 汝州市| 泽州县| 石渠县| 肥东县| 莫力| 德安县| 明星| 柘荣县| 新营市| 延津县| 云林县| 长宁区| 镇康县| 论坛| 英超| 长乐市| 丽水市| 聊城市| 仙桃市| 营山县|