Java語言反射提供壹種動態鏈接程序組件的多功能方法。它允許程序創建和控制任何類的對象(根據安全性限制),無需提前硬編碼目標類。這些特性使得反射特別適用於創建以非常普通的方式與對象協作的庫。例如,反射經常在持續存儲對象為數據庫、XML或其它外部格式的框架中使用。Java reflection 非常有用,它使類和數據結構能按名稱動態檢索相關信息,並允許在運行著的程序中操作這些信息。Java 的這壹特性非常強大,並且是其它壹些常用語言,如 C、C++、Fortran 或者 Pascal 等都不具備的。
但反射有兩個缺點。第壹個是性能問題。用於字段和方法接入時反射要遠慢於直接代碼。性能問題的程度取決於程序中是如何使用反射的。如果它作為程序運行中相對很少涉及的部分,緩慢的性能將不會是壹個問題。即使測試中最壞情況下的計時圖顯示的反射操作只耗用幾微秒。僅反射在性能關鍵的應用的核心邏輯中使用時性能問題才變得至關重要。 許多應用中更嚴重的壹個缺點是使用反射會模糊程序內部實際要發生的事情。程序人員希望在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術會帶來維護問題。反射代碼比相應的直接代碼更復雜,正如性能比較的代碼實例中看到的壹樣。解決這些問題的最佳方案是保守地使用反射——僅在它可以真正增加靈活性的地方——記錄其在目標類中的使用。
利用反射實現類的動態加載
最近在成都寫壹個移動增值項目,俺負責後臺server端。功能很簡單,手機用戶通過GPRS打開Socket與服務器連接,我則根據用戶傳過來的數據做出響應。做過類似項目的兄弟壹定都知道,首先需要定義壹個類似於MSNP的通訊協議,不過今天的話題是如何把這個系統設計得具有高度的擴展性。由於這個項目本身沒有進行過較為完善的客戶溝通和需求分析,所以以後肯定會有很多功能上的擴展,通訊協議肯定會越來越龐大,而我作為壹個不那麽勤快的人,當然不想以後再去修改寫好的程序,所以這個項目是實踐面向對象設計的好機會。
首先定義壹個接口來隔離類:
package org.bromon.reflect;
public interface Operator{
public java.util.List act(java.util.List params)
}
根據設計模式的原理,我們可以為不同的功能編寫不同的類,每個類都繼承Operator接口,客戶端只需要針對Operator接口編程就可以避免很多麻煩。比如這個類:
package org.bromon.reflect.*;
public class Success implements Operator{
public java.util.List act(java.util.List params){
List result=new ArrayList();
result.add(new String(“操作成功”));
return result;
} } 我們還可以寫其他很多類,但是有個問題,接口是無法實例化的,我們必須手動控制具體實例化哪個類,這很不爽,如果能夠向應用程序傳遞壹個參數,讓自己去選擇實例化壹個類,執行它的act方法,那我們的工作就輕松多了。
很幸運,我使用的是Java,只有Java才提供這樣的反射機制,或者說內省機制,可以實現我們的無理要求。編寫壹個配置文件emp.properties:
#成功響應
1000=Success
#向客戶發送普通文本消息
2000=Load
#客戶向服務器發送普通文本消息
3000=Store
文件中的鍵名是客戶將發給我的消息頭,客戶發送1000給我,那麽我就執行Success類的act方法,類似的如果發送2000給我,那就執行Load類的act方法,這樣壹來系統就完全符合開閉原則了,如果要添加新的功能,完全不需要修改已有代碼,只需要在配置文件中添加對應規則,然後編寫新的類,實現act方法就ok,即使我棄這個項目而去,它將來也可以很好的擴展。這樣的系統具備了非常良好的擴展性和可插入性。 下面這個例子體現了動態加載的功能,程序在執行過程中才知道應該實例化哪個類:
package org.bromon.reflect.*;
import java.lang.reflect.*;
public class TestReflect{
//加載配置文件,查詢消息頭對應的類名
private String loadProtocal(String header){
String result=null;
try{
Properties prop=new Properties();
FileInputStream fis=new FileInputStream(emp.properties);
prop.load(fis);
result=prop.getProperty(header);
fis.close();
}catch(Exception e){
System.out.println(e);
}
return result; }
//針對消息作出響應,利用反射導入對應的類
public String response(String header,String content) {
String result=null; String s=null;
try {
/* * 導入屬性文件emp.properties,查詢header所對應的類的名字
* 通過反射機制動態加載匹配的類,所有的類都被Operator接口隔離
* 可以通過修改屬性文件、添加新的類(繼承MsgOperator接口)來擴展協議
*/
s=org.bromon.reflect.+this.loadProtocal(header);
//加載類 Class c=Class.forName(s);
//創建類的事例
Operator mo=(Operator)c.newInstance();
//構造參數列表
Class params[]=new Class[1];
params[0]=Class.forName(java.util.List);
//查詢act方法
Method m=c.getMethod(act,params);
Object args[]=new Object[1];
args[0]=content;
//調用方法並且獲得返回
Object returnObject=m.invoke(mo,args);
}catch(Exception e){
System.out.println(Handler-response:+e);
}
return result;
}
public static void main(String args[]){
TestReflect tr=new TestReflect(); tr.response(args[0],”消息內容”);
} }
測試壹下:java TestReflect 1000 這個程序是針對Operator編程的,所以無需做任何修改,直接提供Load和Store類,就可以支持2000、3000做參數的調用。
有了這樣的內省機制,可以把接口的作用發揮到極至,設計模式也更能體現出威力,而不僅僅供我們飯後閑聊。