說明:本文為孫衛琴的《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.HelloService,methodName=echo
className=remotecall.HelloEcho,methodName=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,阿蜜果"));
}
}
運行后可看到這個代理類是動態生成的。在Spring的AOP中也運到了動態代理機制,有興趣的朋友可查找相關資料。
posted on 2007-09-19 13:21
阿蜜果 閱讀(2753)
評論(3) 編輯 收藏 所屬分類:
Java