Java反射與內省(Introspector)

一、java反射機制
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠得到這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
用一句比較白的話來概括,反射就是讓你可以通過名稱來得到對象 ( 類,屬性,方法 ) 的技術。例如我們可以通過類名來生成一個類的實例;知道了方法名,就可以調用這個方法;知道了屬性名就可以訪問這個屬性的值。
 
Java反射機制主要提供了以下功能:
1、爲一個類生成對應的Class對象
運用(已知對象) getClass():Object類中的方法,每個類都擁有此方法。
如:String str=new String();Class strClass=str.getClass();
運用(已知子類的class) Class.getSuperclass():Class類中的方法,返回該Class的父類的Class;
運用(已知類全名)Class.forName()靜態方法
運用(已知類)類名.class
2、通過類名來構造一個類的實例
   a、調用無參的構造函數:
       Class newoneClass = Class.forName(類全名);
       newoneClass.newInstance();
   b、調用有參的構造函數:我們可以自定義一個函數。
public Object newInstance(String className, Object[] args) throws Exception {
//args爲參數數組
Class newoneClass = Class.forName(className);
//得到參數的Class數組(每個參數的class組成的數組),由此來決定調用那個構造函數
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Constructor cons = newoneClass.getConstructor(argsClass); //根據argsClass選擇函數
return cons.newInstance(args); //根據具體參數實例化對象。
}
 
3、得到某個對象的屬性
  a、非靜態屬性:首先得到class,然後得到這個class具有的field,然後以具體實例爲參數
調用這個field
public Object getProperty(Object owner, String fieldName) throws Exception {
    Class ownerClass = owner.getClass();//首先得到class
    Field field = ownerClass.getField(fieldName);
    //然後得到這個class具有的field,也可以通過getFields()得到所有的field
    Object property = field.get(owner);
    //owner指出了取得那個實例的這個屬性值,如果這個屬性是非公有的,這裏會報IllegalAccessException。
    return property;
}
 
  b、靜態屬性:
只有最後一步不同,由於靜態屬性屬於這個類,所以只要在這個類上調用這個field即可
Object property = field.get(ownerClass);
 
4、執行某對象的方法
public Object invokeMethod(Object owner, String methodName,Object[] args)
throws Exception {
     Class ownerClass = owner.getClass(); //也是從class開始的
      //得到參數的class數組,相當於得到參數列表的類型數組,來取決我們選擇哪個函數。
     Class[] argsClass = new Class[args.length];
     for (int i = 0, j = args.length; i < j; i++) {
         argsClass[i] = args[i].getClass();
     }
//根據函數名和函數類型來選擇函數
     Method method = ownerClass.getMethod(methodName, argsClass);
     return method.invoke(owner, args);//具體實例下,具體參數值下調用此函數
}
5、執行類的靜態方法
和上面的相似只是最後一行不需要指定具體實例
return method.invoke(null, args);
6、判斷是否爲某個類的實例
public boolean isInstance(Object obj, Class cls) {
     return cls.isInstance(obj);
}
二、java內省機制
      內省是 Java 語言對 Bean 類屬性、事件的一種處理方法(也就是說給定一個javabean對象,我們就可以得到/調用它的所有的get/set方法)。例如類 A 中有屬性 name, 那我們可以通過 getName,setName 來得到其值或者設置新的值。通過 getName/setName 來訪問 name 屬性,這就是默認的規則。 Java 中提供了一套 API 用來訪問某個屬性的 getter/setter 方法,通過這些 API 可以使你不需要了解這個規則,這些 API 存放於包 java.beans 中。
       一般的做法是通過類 Introspector 來獲取某個對象的 BeanInfo 信息,然後通過 BeanInfo 來獲取屬性的描述器( PropertyDescriptor ),通過這個屬性描述器就可以獲取某個屬性對應的 getter/setter 方法,然後我們就可以通過反射機制來調用這些方法。下面我們來看一個例子,這個例子把某個對象的所有屬性名稱和值都打印出來:
//定義一個javabean
public class PersonBean {
    public String name;
    public String[] childname;
    public String[] getChildname() {
        return childname;
    }
    public void setChildname(String[] childname) {
        this.childname = childname;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
 
//定義一個測試類,來進行一下set和get方法的調用舉例
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
public class TestIntrospector {
     public static void main(String[] args) throws IllegalArgumentException,IllegalAccessException, SecurityException, NoSuchMethodException,
InvocationTargetException, IntrospectionException {
//演示一下get方法的調用
//初始化一個javabean對象
    PersonBean pb=new PersonBean();
    pb.setName("kangjian");
    String[] childs=new String[]{"kk","jj","nn"};
    pb.setChildname(childs);
    //將該javabean中的屬性放入到BeanInfo中。第二個參數爲截止參數,表示截止到此類之前,不包括此類。如果不設置的話,那麼將會得到本類以及其所有父類的info。
    BeanInfo bi=Introspector.getBeanInfo(pb.getClass(), Object.class);
    //將每個屬性的信息封裝到一個PropertyDescriptor形成一個數組其中包括屬性名字,讀寫方法,屬性的類型等等
   PropertyDescriptor[] pd=bi.getPropertyDescriptors();
//演示如何get
   for (int i = 0; i < pd.length; i++) {
        if(pd[i].getPropertyType().isArray()) //getPropertyType得到屬性類型。
        {
            //getReadMethod()得到此屬性的get方法----Method對象,然後用invoke調用這個方法
            String[] result=(String[]) pd[i].getReadMethod().invoke(pb, null);
            System.out.println(pd[i].getName()+":");//getName得到屬性名字
            for (int j = 0; j < result.length; j++) {
            System.out.println(result[j]);
            }
        }
        else
        {
             System.out.println(pd[i].getName()+":"+pd[i].getReadMethod().invoke(pb, null));
        }
   }
 
    //演示一下set方法的調用
    //初始化一個尚未set的javabean
    PersonBean pb0=new PersonBean();
    //模擬一個數據(數據名字和javabean的屬性名一致)
    String name="luonan";
    String[] childname=new String[]{"luo","nan"};
 
    BeanInfo bi0=Introspector.getBeanInfo(pb0.getClass(), Object.class);
    PropertyDescriptor[] pd0=bi0.getPropertyDescriptors();
 
    for (int i = 0; i < pd0.length; i++) {
       if(pd0[i].getPropertyType().isArray())
        {  
             if(pd0[i].getName().equals("childname"));
             {
                 if(pd0[i].getPropertyType().getComponentType().equals(String.class))
                 {   //getComponentType()可以得到數組類型的元素類型
                     //getWriteMethod()得到此屬性的set方法---Method對象,然後用invoke調用這個方法
                     pd0[i].getWriteMethod().invoke(pb0,new Object[]{childname});
                 }
            }
       }
       else
       {
            if(pd0[i].getName().equals("name"));
            {
                  pd0[i].getWriteMethod().invoke(pb0,name);
            }
      }
   }
 
    System.out.println(pb0.getName());
    String[] array=pb0.getChildname();
    for (int i = 0; i < array.length; i++) {
        System.out.println(array[i]);
    }
  }
}
三、關於內省的思考
struts2的action(還有struts1的formbean)就是這麼實現的。
前臺的form標籤具有一些屬性(在配置文件中知道這個form提交到那個action,而這個action有和這個form相對應的屬性及其 get/set),提交以後,由struts的servlet攔下來轉發給某個具體的action.而在轉發給action之前struts通過內省的方式將form中的值set到了action中去。
其實只要有個set**或者get**,內省就會理解爲存在這樣的**屬性,這樣可以方便我們把 Bean 類通過一個接口來定義而不用去關心具體實現,不用去關心 Bean 中數據的存儲。比如我們可以把所有的 getter/setter 方法放到接口裏定義,但是真正數據的存取則是在具體類中去實現,這樣可提高系統的擴展性。
四、總結
將 Java 的反射以及內省應用到程序設計中去可以大大的提供程序的智能化和可擴展性。有很多項目都是採取這兩種技術來實現其核心功能,例如我們前面提到的 Struts ,還有用於處理 XML 文件的 Digester 項目,其實應該說幾乎所有的項目都或多或少的採用這兩種技術。在實際應用過程中二者要相互結合方能發揮真正的智能化以及高度可擴展性。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章