Java內省機制

from URl : http://hi.baidu.com/suny_duan/blog/item/35e64489ac47af9fa5c2724c.html

 

1).內省(Introspector)是 Java 語言對 Bean 類屬性、事件的一種缺省處理方法。例如類 A 中有屬性 name, 那我們可以通過 getName,setName 來得到其值或者設置新的值。通過 getName/setName 來訪問 name 屬性,這就是默認的規則。 Java 中提供了一套 API 用來訪問某個屬性的 getter/setter 方法,通過這些 API 可以使你不需要了解這個規則(但你最好還是要搞清楚),這些 API 存放於包 java.beans 中。

2).直接通過屬性的描述器java.beans.PropertyDescriptor類,來訪問屬性的getter/setter 方法;

相關代碼:
public class Point {
private Integer x;
private Integer y;

public Point(Integer x, Integer y) {
    super();
    this.x = x;
    this.y = y;
}

public Integer getX() {
    return x;
}

public void setX(Integer x) {
    this.x = x;
}

public Integer getY() {
    return y;
}

public void setY(Integer y) {
    this.y = y;
}
}

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

public class Reflect {

public static void main(String[] args) throws Exception {
    Point point = new Point(2, 5);
    String proName = "x";

    getProperty(point, proName);
    setProperty(point, proName);

}

private static void setProperty(Point point, String proName) throws Exception {
    PropertyDescriptor proDescriptor = new PropertyDescriptor(proName, Point.class);
    Method methodSetX = proDescriptor.getWriteMethod();
    methodSetX.invoke(point, 8);
    System.out.println(point.getX());// 8
}

private static void getProperty(Point point, String proName) throws Exception {
    PropertyDescriptor proDescriptor = new PropertyDescriptor(proName, Point.class);
    Method methodGetX = proDescriptor.getReadMethod();
    Object objx = methodGetX.invoke(point);
    System.out.println(objx);// 2
}
}

3).通過類 Introspector 來獲取某個對象的 BeanInfo 信息,然後通過 BeanInfo 來獲取屬性的描述器( PropertyDescriptor ),通過這個屬性描述器就可以獲取某個屬性對應的 getter/setter 方法,然後我們就可以通過反射機制來調用這些方法。

相關代碼:
把2中的getProperty()修改成如下形式:
private static void getProperty(Point point, String proName) throws Exception {
    BeanInfo beanInfo = Introspector.getBeanInfo(point.getClass());
    PropertyDescriptor[] proDescriptors = beanInfo.getPropertyDescriptors();
    for(PropertyDescriptor prop: proDescriptors){
      if(prop.getName().equals(proName)){
        Method methodGetx = prop.getReadMethod();
        System.out.println(methodGetx.invoke(point));//8
        break;
      }
    }
}

4).我們又通常把javabean的實例對象稱之爲值對象 (Value Object),因爲這些bean中通常只有一些信息字段和存儲方法,沒有功能性方法。一個JavaBean類可以不當JavaBean用,而當成普通類 用。JavaBean實際就是一種規範,當一個類滿足這個規範,這個類就能被其它特定的類調用。一個類被當作javaBean使用時,JavaBean的 屬性是根據方法名推斷出來的,它根本看不到java類內部的成員變量(javabean的成員變量通常都是私有private的)。

5).除了反射用到的類需要引入外,內省需要引入的類如下所示,它們都屬於java.beans包中的類,自己寫程序的時候也不能忘了引入相應的包或者類。
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

6).下面講解一些開源的工具類Beanutils,需要額外下載的,commons-beanutils.jar,要使用它還必須導入commons-logging.jar包,不然會出異常;
相關代碼一:
public static void main(String[] args) throws Exception {
    Point point = new Point(2, 5);
    String proName = "x";
    BeanUtils.setProperty(point, proName, "8");
    System.out.println(point.getX());// 8
    System.out.println(BeanUtils.getProperty(point, proName));// 8
    System.out.println(BeanUtils.getProperty(point, proName).getClass().getName());// java.lang.String

    BeanUtils.setProperty(point, proName, 8);
    System.out.println(BeanUtils.getProperty(point, proName).getClass().getName());// java.lang.String
}
//我們看到雖然屬性x的類型是Integer,但是我們設置的時候無論是Integer還是String,BeanUtils的內部都是當成String來處理的。

相關代碼二:
BeanUtils支持javabean屬性的級聯操作;
public static void main(String[] args) throws Exception {
    Point point = new Point(2, 5);//在point中加一個屬性 private Date birth = new Date();併產生setter/getter方法
    String proName = "birth";
    Date date= new Date();
    date.setTime(10000);
    BeanUtils.setProperty(point, proName, date);
    System.out.println(BeanUtils.getProperty(point, proName));
   
    BeanUtils.setProperty(point, "birth.time", 10000);
    System.out.println(BeanUtils.getProperty(point, "birth.time"));//10000
}
//之所以可以 BeanUtils.setProperty(point, "birth.time", 10000);這樣寫,那是因爲Date類中有getTime()和setTime()方法,即Date類中相當於有time這個屬性。

相關代碼三:
BeanUtils和PropertyUtils對比:
public static void main(String[] args) throws Exception {
    Point point = new Point(2, 5);
    String proName = "x";
    BeanUtils.setProperty(point, proName, "8");
    System.out.println(BeanUtils.getProperty(point, proName));//8
    System.out.println(BeanUtils.getProperty(point, proName).getClass().getName());//java.lang.String
   
// PropertyUtils.setProperty(point, proName, "8");//exception:argument type mismatch
    PropertyUtils.setProperty(point, proName, 8);
    System.out.println(PropertyUtils.getProperty(point, proName));//8
    System.out.println(PropertyUtils.getProperty(point, proName).getClass().getName());//java.lang.Integer
}
//BeanUtils它以字符串的形式對javabean進行轉換,而PropertyUtils是以原本的類型對javabean進行操作。如果類型不對,就會有argument type mismatch異常。

6).理解了相應的原理,那些現成的工具用起來就會更舒服,如Beanutils與PropertyUtils工具。這兩個工具設置屬性的時候一個主要區 別是PropertyUtils.getPropety方法獲得的屬性值的類型爲該屬性本來的類型,而BeanUtils.getProperty則是將 該屬性的值轉換成字符串後才返回。

總結

Web 開發框架 Struts 中的 FormBean 就是通過內省機制來將表單中的數據映射到類的屬性上,因此要求 FormBean 的每個屬性要有 getter/setter 方法。但也並不總是這樣,什麼意思呢?就是說對一個 Bean 類來講,我可以沒有屬性,但是只要有 getter/setter 方法中的其中一個,那麼 Java 的內省機制就會認爲存在一個屬性,比如類中有方法 setMobile ,那麼就認爲存在一個 mobile 的屬性。

將 Java 的反射以及內省應用到程序設計中去可以大大的提供程序的智能化和可擴展性。有很多項目都是採取這兩種技術來實現其核心功能,例如我們前面提到的 Struts ,還有用於處理 XML 文件的 Digester 項目,其實應該說幾乎所有的項目都或多或少的採用這兩種技術。在實際應用過程中二者要相互結合方能發揮真正的智能化以及高度可擴展性。

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