Java內(nèi)省技術(shù)Introspector


首先清楚內(nèi)省是什么?

內(nèi)省(Introspector) 是Java 語(yǔ)言對(duì) JavaBean 類屬性、事件的一種缺省處理方法。
  JavaBean是一種特殊的類,主要用于傳遞數(shù)據(jù)信息,這種類中的方法主要用于訪問(wèn)私有的字段,且方法名符合某種命名規(guī)則。如果在兩個(gè)模塊之間傳遞信息,可以將信息封裝進(jìn)JavaBean中,這種對(duì)象稱為“值對(duì)象”(Value Object),或“VO”。方法比較少。這些信息儲(chǔ)存在類的私有變量中,通過(guò)set()、get()獲得。
 例如類UserInfo

package com.peidasoft.Introspector;

public class UserInfo {
    
    private long userId;
    private String userName;
    private int age;
    private String emailAddress;
    
    public long getUserId() {
        return userId;
    }
    public void setUserId(long userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getEmailAddress() {
        return emailAddress;
    }
    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
    
}

在類UserInfo中有屬性 userName, 那我們可以通過(guò) getUserName,setUserName來(lái)得到其值或者設(shè)置新的值。通過(guò) getUserName/setUserName來(lái)訪問(wèn) userName屬性,這就是默認(rèn)的規(guī)則。 Java JDK中提供了一套 API 用來(lái)訪問(wèn)某個(gè)屬性的 getter/setter 方法,這就是內(nèi)省。

JDK內(nèi)省類庫(kù):

PropertyDescriptor類:
PropertyDescriptor類表示JavaBean類通過(guò)存儲(chǔ)器導(dǎo)出一個(gè)屬性。主要方法:

  1. getPropertyType(),獲得屬性的Class對(duì)象;
  2. getReadMethod(),獲得用于讀取屬性值的方法;getWriteMethod(),獲得用于寫(xiě)入屬性值的方法;
  3. hashCode(),獲取對(duì)象的哈希值;
  4. setReadMethod(Method readMethod),設(shè)置用于讀取屬性值的方法;
  5. setWriteMethod(Method writeMethod),設(shè)置用于寫(xiě)入屬性值的方法。

實(shí)例代碼如下:

package com.peidasoft.Introspector;

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


public class BeanInfoUtil {
        
    public static void setPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
        BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
        PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
        if(proDescrtptors!=null&&proDescrtptors.length>0){
            for(PropertyDescriptor propDesc:proDescrtptors){
                if(propDesc.getName().equals(userName)){
                    Method methodSetUserName=propDesc.getWriteMethod();
                    methodSetUserName.invoke(userInfo, "alan");
                    System.out.println("set userName:"+userInfo.getUserName());
                    break;
                }
            }
        }
    }
    
    public static void getPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
        BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
        PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
        if(proDescrtptors!=null&&proDescrtptors.length>0){
            for(PropertyDescriptor propDesc:proDescrtptors){
                if(propDesc.getName().equals(userName)){
                    Method methodGetUserName=propDesc.getReadMethod();
                    Object objUserName=methodGetUserName.invoke(userInfo);
                    System.out.println("get userName:"+objUserName.toString());
                    break;
                }
            }
        }
    }
    
}

Introspector類:
 將JavaBean中的屬性封裝起來(lái)進(jìn)行操作。在程序把一個(gè)類當(dāng)做JavaBean來(lái)看,就是調(diào)用Introspector.getBeanInfo()方法,得到的BeanInfo對(duì)象封裝了把這個(gè)類當(dāng)做JavaBean看的結(jié)果信息,即屬性的信息。

getPropertyDescriptors(),獲得屬性的描述,可以采用遍歷BeanInfo的方法,來(lái)查找、設(shè)置類的屬性。具體代碼如下:

package com.peidasoft.Introspector;

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


public class BeanInfoUtil {
        
    public static void setPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
        BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
        PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
        if(proDescrtptors!=null&&proDescrtptors.length>0){
            for(PropertyDescriptor propDesc:proDescrtptors){
                if(propDesc.getName().equals(userName)){
                    Method methodSetUserName=propDesc.getWriteMethod();
                    methodSetUserName.invoke(userInfo, "alan");
                    System.out.println("set userName:"+userInfo.getUserName());
                    break;
                }
            }
        }
    }
    
    public static void getPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
        BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
        PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
        if(proDescrtptors!=null&&proDescrtptors.length>0){
            for(PropertyDescriptor propDesc:proDescrtptors){
                if(propDesc.getName().equals(userName)){
                    Method methodGetUserName=propDesc.getReadMethod();
                    Object objUserName=methodGetUserName.invoke(userInfo);
                    System.out.println("get userName:"+objUserName.toString());
                    break;
                }
            }
        }
    }
    
}

通過(guò)這兩個(gè)類的比較可以看出,都是需要獲得PropertyDescriptor,只是方式不一樣:前者通過(guò)創(chuàng)建對(duì)象直接獲得,后者需要遍歷,所以使用PropertyDescriptor類更加方便。

使用實(shí)例:

package com.peidasoft.Introspector;

public class BeanInfoTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        UserInfo userInfo=new UserInfo();
        userInfo.setUserName("peida");
        try {
            BeanInfoUtil.getProperty(userInfo, "userName");
            
            BeanInfoUtil.setProperty(userInfo, "userName");
            
            BeanInfoUtil.getProperty(userInfo, "userName");
            
            BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName");            
            
            BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName");
            
            BeanInfoUtil.setProperty(userInfo, "age");
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

輸出:

get userName:peida
set userName:wong
get userName:wong
set userName:alan
get userName:alan
java.lang.IllegalArgumentException: argument type mismatch
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.peidasoft.Introspector.BeanInfoUtil.setProperty(BeanInfoUtil.java:14)
    at com.peidasoft.Introspector.BeanInfoTest.main(BeanInfoTest.java:22)

 說(shuō)明:BeanInfoUtil.setProperty(userInfo, "age");報(bào)錯(cuò)是應(yīng)為age屬性是int數(shù)據(jù)類型,而setProperty方法里面默認(rèn)給age屬性賦的值是String類型。所以會(huì)爆出argument type mismatch參數(shù)類型不匹配的錯(cuò)誤信息。


BeanUtils工具包:
由上述可看出,內(nèi)省操作非常的繁瑣,所以所以Apache開(kāi)發(fā)了一套簡(jiǎn)單、易用的API來(lái)操作Bean的屬性——BeanUtils工具包?! eanUtils工具包:下載:http://commons.apache.org/beanutils/ 注意:應(yīng)用的時(shí)候還需要一個(gè)logging包 http://commons.apache.org/logging/  使用BeanUtils工具包完成上面的測(cè)試代碼:

package com.peidasoft.Beanutil;

import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;

import com.peidasoft.Introspector.UserInfo;

public class BeanUtilTest {
    public static void main(String[] args) {
        UserInfo userInfo=new UserInfo();
         try {
            BeanUtils.setProperty(userInfo, "userName", "peida");
            
            System.out.println("set userName:"+userInfo.getUserName());
            
            System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName"));
            
            BeanUtils.setProperty(userInfo, "age", 18);
            System.out.println("set age:"+userInfo.getAge());
            
            System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age"));
             
            System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName());
            System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName());
            
            PropertyUtils.setProperty(userInfo, "age", 8);
            System.out.println(PropertyUtils.getProperty(userInfo, "age"));
            
            System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName());
                  
            PropertyUtils.setProperty(userInfo, "age", "8");   
        } 
         catch (IllegalAccessException e) {
            e.printStackTrace();
        } 
         catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

運(yùn)行結(jié)果:

set userName:peida
get userName:peida
set age:18
get age:18
get userName type:java.lang.String
get age type:java.lang.String
java.lang.Integer
Exception in thread "main" java.lang.IllegalArgumentException: Cannot invoke com.peidasoft.Introspector.UserInfo.setAge 
on bean class 'class com.peidasoft.Introspector.UserInfo' - argument type mismatch - had objects of type "java.lang.String" 
but expected signature "int"
    at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2235)
    at org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:2151)
    at org.apache.commons.beanutils.PropertyUtilsBean.setNestedProperty(PropertyUtilsBean.java:1957)
    at org.apache.commons.beanutils.PropertyUtilsBean.setProperty(PropertyUtilsBean.java:2064)
    at org.apache.commons.beanutils.PropertyUtils.setProperty(PropertyUtils.java:858)
    at com.peidasoft.orm.Beanutil.BeanUtilTest.main(BeanUtilTest.java:38)
Caused by: java.lang.IllegalArgumentException: argument type mismatch
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2170)
    ... 5 more

說(shuō)明:

1.獲得屬性的值,例如,
BeanUtils.getProperty(userInfo,"userName"),返回字符串
2.設(shè)置屬性的值,例如,
BeanUtils.setProperty(userInfo,"age",8),參數(shù)是字符串或基本類型自動(dòng)包裝。設(shè)置屬性的值是字符串,獲得的值也是字符串,不是基本類型。   
3.BeanUtils的特點(diǎn):
    1). 對(duì)基本數(shù)據(jù)類型的屬性的操作:在WEB開(kāi)發(fā)、使用中,錄入和顯示時(shí),值會(huì)被轉(zhuǎn)換成字符串,但底層運(yùn)算用的是基本類型,這些類型轉(zhuǎn)到動(dòng)作由BeanUtils自動(dòng)完成。
    2). 對(duì)引用數(shù)據(jù)類型的屬性的操作:首先在類中必須有對(duì)象,不能是null,例如,private Date birthday=new Date();。操作的是對(duì)象的屬性而不是整個(gè)對(duì)象,例如,BeanUtils.setProperty(userInfo,"birthday.time",111111);

package com.peidasoft.Introspector;
import java.util.Date;

public class UserInfo {

    private Date birthday = new Date();
    
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public Date getBirthday() {
        return birthday;
    }      
}


package com.peidasoft.Beanutil;

import java.lang.reflect.InvocationTargetException;
import org.apache.commons.beanutils.BeanUtils;
import com.peidasoft.Introspector.UserInfo;

public class BeanUtilTest {
    public static void main(String[] args) {
        UserInfo userInfo=new UserInfo();
         try {
            BeanUtils.setProperty(userInfo, "birthday.time","111111");  
            Object obj = BeanUtils.getProperty(userInfo, "birthday.time");  
            System.out.println(obj);          
        } 
         catch (IllegalAccessException e) {
            e.printStackTrace();
        } 
         catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

3.PropertyUtils類和BeanUtils不同在于,運(yùn)行g(shù)etProperty、setProperty操作時(shí),沒(méi)有類型轉(zhuǎn)換,使用屬性的原有類型或者包裝類。由于age屬性的數(shù)據(jù)類型是int,所以方法PropertyUtils.setProperty(userInfo, "age", "8")會(huì)爆出數(shù)據(jù)類型不匹配,無(wú)法將值賦給屬性。
轉(zhuǎn)載 : 附上原文鏈接
<small>http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html</small>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容