C語言中文網 目錄

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

上一節詳細介紹了如何使用 java.lang.reflect 包提供的 Constructor 類獲取構造方法的信息、使用 Method 類獲取成員方法的信息以及使用 Field 類獲取成員變量的信息。

本案例將介紹反射機制在網絡編程中的應用,實現如何在客戶端通過遠程方法調用服務器端的方法。

假定在服務器端有一個 HelloService 接口,該接口具有 getTime() 和 echo() 方法,具體代碼如下:
package ch12;
import java.util.Date;
public interface HelloService
{
    public String echo(String msg);
    public Date getTime();
}

在服務器上創建一個 HelloServiceImpl 類并實現 HelloService 接口。HelloServiceImpl 類的代碼如下:
package ch12;
import java.util.Date;
public class HelloServiceImpl implements HelloService
{
    @Override
    public String echo(String msg)
    {
        return "echo:"+msg;
    }
    @Override
    public Date getTime()
    {
        return new Date();
    }
}

上述代碼所示,在 HelloServiceImpl 類中對 echo() 方法和 getTime() 方法進行了重寫。那么,客戶端如何調用服務器端 Hello-ServiceImpl 類中的 getTime() 和 echo() 方法呢?

具體方法是:客戶端需要把調用的方法名、方法參數類型、方法參數值,以及方法所屬的類名或接口名發送給服務器端。服務器端再調用相關對象的方法,然后把方法的返回值發送給客戶端。

為了便于按照面向對象的方式來處理客戶端與服務器端的通信,可以把它們發送的信息用 Call 類來表示。一個 Call 對象表示客戶端發起的一個遠程調用,它包括調用的類名或接口名、方法名、方法參數類型、方法參數值和方法執行結果。

Call 類的實現代碼如下:
package ch12;
import java.io.Serializable;
public class Call implements Serializable
{
    private String className;    //表示類名或接口名
    private String methodName;    //表示方法名
    private Class[] paramTypes;    //表示方法參數類型
    private Object[] params;    //表示方法參數值

    // 表示方法的執行結果
    // 如果方法正常執行,則result為方法返回值,如果方法拋出異常,那么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;
    }
    public String getClassName()
    {
        return className;
    }
    public void setClassName(String className)
    {
        this.className=className;
    }
    public String getMethodName()
    {
        return methodName;
    }

    public void setMethodName(String methodName)
    {
        this.methodName=methodName;
    }
    public Class[] getParamTypes()
    {
        return paramTypes;
    }
    public void setParamTypes(Class[] paramTypes)
    {
        this.paramTypes=paramTypes;
    }
    public Object[] getParams()
    {
        return params;
    }
    public void setParams(Object[] params)
    {
        this.params=params;
    }
    public Object getResult()
    {
        return result;
    }
    public void setResult(Object result)
    {
        this.result=result;
    }
    public String toString()
    {
        return "className="+className+"methodName="+methodName;
    }
}

假設客戶端為 SimpleClient,服務器端為 SimpleServer。SimpleClient 調用 SimpleServer 的 HelloServiceImpl 對象中 echo() 方法的流程如下:
  1. SimpleClient 創建一個 Call 對象,它包含調用 HelloService 接口的 echo() 方法的信息。
  2. SimpleClient 通過對象輸出流把 Call 對象發送給 SimpleServer。
  3. SimpleServer 通過對象輸入流讀取 Call 對象,運用反射機制調用 HelloServiceImpl 對象的 echo() 方法,把 echo() 方法的執行結果保存到 Call 對象中。
  4. SimpleServer 通過對象輸出流把包含方法執行結果的 Call 對象發送給 SimpleClient。
  5. SimpleClient 通過對象輸入流讀取 Call 對象,從中獲得方法執行結果。

首先來看看客戶端程序 SimpleClient 類的實現代碼。
package ch12;
import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.*;
import java.io.*;
import java.net.*;
import java.util.*;
public class SimpleClient
{
    public void invoke() 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 call=new Call("ch12.HelloService","echo",new Class[]{String.class},new Object[]{"Java"});
        oos.writeObject(call);    //向服務器發送Call對象
        call=(Call) ois.readObject();    //接收包含了方法執行結果的Call對象
        System.out.println(call.getResult());
        ois.close();
        oos.close();
        socket.close();
    }
    public static void main(String args[]) throws Exception
    {
        new SimpleClient().invoke();
    }
}

如上述代碼所示,客戶端 SimpleClient 類的主要作用是建立與服務器的連接,然后將帶有調用信息的 Call 對象發送到服務器端。

服務器端 SimpleServer 類在收到調用請求之后會使用反射機制動態調用指定對象的指定方法,再將執行結果返回給客戶端。

SimpleServer 類的實現代碼如下:
package ch12;
import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.*;
public class SimpleServer
{
    private Map remoteObjects=new HashMap();    //存放遠程對象的緩存
    /** 把一個遠程對象放到緩存中 */
    public void register(String className,Object remoteObject)
    {
        remoteObjects.put(className,remoteObject);
    }
    public void 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) ois.readObject();    //接收客戶發送的Call對象
            System.out.println(call);
            call=invoke(call);    //調用相關對象的方法
            oos.writeObject(call);    //向客戶發送包含了執行結果的Call對象
            ois.close();
            oos.close();
            socket.close();
        }
    }
    public Call invoke(Call call)
    {
        Object result=null;
        try
        {
            String className=call.getClassName();
            String methodName=call.getMethodName();
            Object[] params=call.getParams();
            Class classType=Class.forName(className);
            Class[] paramTypes=call.getParamTypes();
            Method method=classType.getMethod(methodName, paramTypes);
            Object remoteObject=remoteObjects.get(className);    //從緩存中取出相關的遠程對象
            if(remoteObject==null)
            {
                throw new Exception(className+"的遠程對象不存在");
            }
            else
            {
                result=method.invoke(remoteObject,params);
            }
        }
        catch(Exception e)
        {
            result=e;
        }
        call.setResult(result);    //設置方法執行結果
        return call;
    }
    public static void main(String args[]) throws Exception
    {
        SimpleServer server=new SimpleServer();
        // 把事先創建的HelloServiceImpl對象加入到服務器的緩存中
        server.register("ch13.HelloService", new HelloServiceImpl());
        server.service();
    }
}

由于這是一個網絡程序,首先需要運行服務器端 SimpleServer,然后再運行客戶端 SimpleClient。運行結果是在客戶端看到輸出“echoJava”,這個結果是服務器端執行 HelloServicelmpl 對象的 echo() 方法的返回值。圖 1 所示顯示了 SimpleClient 與 SimpleServer 的通信過程。


圖1 SimpleClient與SimpleServer的通信過程

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo