標籤:cep 配置 out 執行 pack 添加 throw method 基礎
java的內省機制和反射機制什麼區別
內省操作只針對JavaBean,只有符合JavaBean規則的類的成員才可以採用內省API進行操作。。。。而反射則不同,一個類的所有成員都可以進行反射操作。
內省和反射的操作也有很大不同,內省是先得到屬性描述器PropertyDecriptor後再進行各種操作,反射則是先得到類的位元組碼Class後再進行各種操作的。
反射(reflection)(實現可擴充性
智能化
)
相對而言,反射比內省更容易理解一點。用一句比較白的話來概括,反射就是讓你可以通過名稱來得到對象(類,屬性,方法)的技術。例如我們可以通過類名來產生一個類的執行個體;知道了方法名,就可以調用這個方法;知道了屬性名稱就可以訪問這個屬性的值,還是寫兩個例子讓大家更直觀的瞭解反射的使用方法:
//通過類名來構造一個類的執行個體 ClassClasscls_str=Class.forName("java.lang.String"); //上面這句很眼熟,因為使用過JDBC訪問資料庫的人都用過J Objectstr=cls_str.newInstance(); //相當於Stringstr=newString(); //通過方法名來調用一個方法 StringmethodName="length"; Methodm=cls_str.getMethod(methodName,null); System.out.println("lengthis"+m.invoke(str,null)); //相當於System.out.println(str.length());
上面的兩個例子是比較常用方法。看到上面的例子就有人要發問了:為什麼要這麼麻煩呢?本來一條語句就完成的事情幹嗎要整這麼複雜?沒錯,在上面的例子中確實沒有必要這麼麻煩。不過你想像這樣一個應用程式,它支援動態功能擴充,也就是說程式不重新啟動但是可以自動載入新的功能,這個功能使用一個具體類來表示。首先我們必須為這些功能定義一個介面類,然後我們要求所有擴充的功能類必須實現我指定的介面,這個規定了應用程式和可擴充功能之間的介面規則,但是怎麼動態載入呢?我們必須讓應用程式知道要擴充的功能類的類名,比如是test.Func1,當我們把這個類名(字串)告訴應用程式後,它就可以使用我們第一個例子的方法來載入並啟用新的功能。這就是類的反射,請問你有別的選擇嗎?
應用:Java中利用反射實作類別的動態載入
//首先定義一個介面來隔離類:public interface Operator{// public java.util.List act(java.util.List params); public java.util.List act(String content,String content2,java.util.List params);}
根據設計模式的原理,我們可以為不同的功能編寫不同的類,每個類都繼承Operator介面,用戶端只需要針對Operator介面編程就可以避免很多麻煩。比如這個類:
import java.util.*;public class Success implements Operator{
public static void main(String[] args) { List list = new ArrayList(); list.add("Success3"); Operator op = new Success(); System.out.println("act===" + op.act("Success1", "Success2", list));}//實現介面的方法// public java.util.List act(java.util.List params)public java.util.List act(String content, String content2,java.util.List params) { List result = new ArrayList(); result.add(content); result.add(content2); result.add(params); return result;}}
同樣,也可以寫另一個類:
import java.util.*;public class Load implements Operator{public static void main(String[] args) { List list = new ArrayList(); list.add("Load3"); Operator op = new Load(); System.out.println("act===" + op.act("Load1", "Load2", list));}// public java.util.List act(java.util.List params)public java.util.List act(String content, String content2,java.util.List params){ List result = new ArrayList(); result.add(content); result.add(content2); result.add(params); return result;}}
我們還可以寫其他很多類,但是有個問題,介面是無法執行個體化的,我們必須手動控制具體執行個體化哪個類,這很不爽,如果能夠嚮應用程式傳遞一個參數,讓自己去選擇執行個體化一個類,執行它的act方法,那我們的工作就輕鬆多了
很幸運,Java提供這樣的反射機制,可以實現我們的無理要求。編寫一個設定檔emp.properties:
#成功響應
1000=Success
#向客戶發送普通簡訊
2000=Load
#客戶向伺服器發送普通簡訊
3000=Store
檔案中的鍵名是客戶將發給我的訊息頭,客戶發送1000給我,那麼,我就執行Success類的act方法,類似的如果發送2000給我,那就執行Load類的act方法,這樣一來系統就完全符合開閉原則了,如果要添加新的功能,完全不需要修改已有代碼,只需要在設定檔中添加對應規則,然後編寫新的類,實現act方法就ok,即使我棄這個項目而去,它將來也可以很好的擴充。這樣的系統具備了非常良好的擴充性和可插入性。
下面這個例子體現了動態載入的功能,程式在執行過程中才知道應該執行個體化哪個類
import java.lang.reflect.*;import java.util.Properties;import java.io.FileInputStream;import java.util.List;//這個程式是針對Operator編程的,所以無需做任何修改,直接提供Load和Store類,就可以支援2000、3000做參數的調用//有了這樣的反射機制,可以把介面的作用發揮到極至,設計模式也更能體現出威力,public class TestReflect{//載入設定檔,查詢訊息頭對應的類名private String loadProtocal(String header){ String result=null; try { Properties prop=new Properties(); // FileInputStream fis=new FileInputStream("emp.properties"); // id = prop.getProperty(idString); // prop.load(fis); // fis.close(); prop.load(getTCL().getResourceAsStream("emp.properties")); result=prop.getProperty(header); }catch(Exception e){ System.out.println(e); }return result;}private static ClassLoader getTCL() throws IllegalAccessException,InvocationTargetException { Method method = null; try { method = Thread.class.getMethod("getContextClassLoader", null); } catch (NoSuchMethodException e) { return null; }return (ClassLoader)method.invoke(Thread.currentThread(), null);}//針對訊息作出響應,利用反射匯入對應的類public String response(String header,String content,String content2,List list){ String result=null; String s=null; try { /* * 匯入屬性檔案emp.properties,查詢header所對應的類的名字 * 通過反射機制動態載入匹配的類,所有的類都被Operator介面隔離 * 可以通過修改屬性檔案、添加新的類(繼承MsgOperator介面)來擴充協議 */ s="org.bromon.reflect."+this.loadProtocal(header).trim(); //載入類 System.out.println("s==="+s);//列印 s===org.bromon.reflect.Success Class c=Class.forName(s); //java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類 // Method m[] = c.getDeclaredMethods();// // for (int i = 0; i < m.length; i++)// // System.out.println(m[i].toString()); // 列印 public java.util.List org.bromon.reflect.Success.act(java.util.List) //建立類的案例 Operator mo=(Operator)c.newInstance(); System.out.println("mo==="+mo); //構造參數列表 Class params[]=new Class[3]; // params[0]=Class.forName("java.util.List"); params[0]=Class.forName("java.lang.String"); params[1]=Class.forName("java.lang.String"); params[2]=Class.forName("java.util.List"); System.out.println("params[0]==="+params[0]); // //查詢act方法 Method m=c.getMethod("act",params); System.out.println("method=="+m.toString()); Object[] args=new Object[3]; args[0]=content; args[1]=content2; args[2]=list; // //調用方法並且獲得返回 Object returnObject=m.invoke(mo,args);//這個地方出問題了,拋異常~~~~ // System.out.println("returnObject==="+returnObject); List result2 = (List)returnObject; result = (String)result2.get(0); System.out.println("result2=="+result2); // }catch(Exception e) { System.out.println("Handler-response:"+e); //Handler-response:java.lang.IllegalArgumentException: argument type mismatch //IllegalArgumentException - 如果該方法是執行個體方法,且指定對象參數不是聲明基礎方法的類或介面(或其中的子類或實現程式)的執行個體; //如果實參和形參的數量不相同;如果基本參數的解包轉換失敗;或者如果在解包後,無法通過方法調用轉換將參數值轉換為相應的形參類型。 }return result;}public static void main(String args[]){ TestReflect tr=new TestReflect(); List list = new java.util.ArrayList(); list.add("測試List"); tr.response("2000","Load1","Load2",list);//1000是Success,2000是Load tr.response("1000","Success1","Success2",list);//1000是Success,2000是Load}}
測試一下,run一下TestReflect類,列印內容有,great!!
result2==[Load1, Load2, [測試List]]
result2==[Success1, Success2, [測試List]]
這個程式是針對Operator編程的,所以無需做任何修改,直接提供Load和Store類,就可以支援2000、3000做參數的調用。
有了這樣的反射機制,可以把介面的作用發揮到極至,設計模式也更能體現出威力
內省(Introspector
)
內省操作只針對JavaBean,只有符合JavaBean規則的類的成員才可以採用內省API進行操作
內省是Java語言對Bean類屬性、事件的一種預設處理方法。例如類A中有屬性name,那我們可以通過getName,setName來得到其值或者設定新的值。通過getName/setName來訪問name屬性,這就是預設的規則。Java中提供了一套API用來訪問某個屬性的getter/setter方法,通過這些API可以使你不需要瞭解這個規則,這些API存放於包java.beans中。
一般的做法是通過類Introspector來擷取某個對象的BeanInfo資訊,然後通過BeanInfo來擷取屬性的描述器(PropertyDescriptor),通過這個屬性描述器就可以擷取某個屬性對應的getter/setter方法,然後我們就可以調用這些方法。下面我們來看一個例子,這個例子把某個對象的所有屬性名稱和值都列印出來:
/* *Createdon2004-6-29 */ package demo; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; public class IntrospectorDemo{ String name; public static void main(String[]args)throwsException{
IntrospectorDemo demo=new IntrospectorDemo(); demo.setName("WinterLau"); //如果不想把父類的屬性也列出來的話, //那getBeanInfo的第二個參數填寫父類的資訊 BeanInfo bi=Introspector.getBeanInfo(demo.getClass(),Object.class); //通過類 Introspector來擷取某個對象的BeanInfo資訊 PropertyDescriptor[] props=bi.getPropertyDescriptors(); //通過BeanInfo來擷取 屬性的描述器 for(inti=0;i<props.length;i++){ //通過這個屬性描述器就可以擷取某個屬性對應的getter/setter方法 //然後我們就可以通過反射機制來調用這些方法 System.out.println(props[i].getName()+"="+props[i].getReadMethod().invoke(demo,null)); } } public String getName(){ return name; } public void setName(Stringname){ this.name=name; } }
應用:
通過內省機制來將表單中的資料對應到類的屬性上
Web開發架構Struts中的FormBean就是通過內省機制來將表單中的資料對應到類的屬性上,因此要求FormBean的每個屬性要有getter/setter方法。但也並不總是這樣,什麼意思呢?就是說對一個Bean類來講,我可以沒有屬性,但是只要有getter/setter方法中的其中一個,那麼Java的內省機制就會認為存在一個屬性,比如類中有方法setMobile,那麼就認為存在一個mobile的屬性,這樣可以方便我們把Bean類通過一個介面來定義而不用去關心具體實現,不用去關心Bean中資料的儲存。比如我們可以把所有的getter/setter方法放到介面裡定義,但是真正資料的存取則是在具體類中去實現,這樣可提高系統的擴充性。
總結
將Java的反射以及內省應用到程式設計中去可以大大的提供者的可擴充性和智能化。有很多項目都是採取這兩種技術來實現其核心功能,例如我們前面提到的Struts,還有用於處理XML檔案的Digester項目,其實應該說幾乎所有的項目都或多或少的採用這兩種技術。在實際應用過程中二者要相互結合方能發揮真正的智能化以及高度可擴充性。
理解Java的反射與內省及其區別