深刻理解Java:內省(Introspector)

內省(Introspector) 是Java 語言對 JavaBean 類屬性、事件的一種缺省處理方法。html

  JavaBean是一種特殊的類,主要用於傳遞數據信息,這種類中的方法主要用於訪問私有的字段,且方法名符合某種命名規則。若是在兩個模塊之間傳遞信息,能夠將信息封裝進JavaBean中,這種對象稱爲「值對象」(Value Object),或「VO」。方法比較少。這些信息儲存在類的私有變量中,經過set()、get()得到。java

  例如類UserInfo :apache

 1 package com.peidasoft.Introspector;
 2 
 3 public class UserInfo {
 4     
 5     private long userId;
 6     private String userName;
 7     private int age;
 8     private String emailAddress;
 9     
10     public long getUserId() {
11         return userId;
12     }
13     public void setUserId(long userId) {
14         this.userId = userId;
15     }
16     public String getUserName() {
17         return userName;
18     }
19     public void setUserName(String userName) {
20         this.userName = userName;
21     }
22     public int getAge() {
23         return age;
24     }
25     public void setAge(int age) {
26         this.age = age;
27     }
28     public String getEmailAddress() {
29         return emailAddress;
30     }
31     public void setEmailAddress(String emailAddress) {
32         this.emailAddress = emailAddress;
33     }
34     
35 }

在類UserInfo中有屬性 userName, 那咱們能夠經過 getUserName,setUserName來獲得其值或者設置新的值。經過 getUserName/setUserName來訪問 userName屬性,這就是默認的規則。 Java JDK中提供了一套 API 用來訪問某個屬性的 getter/setter 方法,這就是內省。工具

  JDK內省類庫:測試


  PropertyDescriptor類:this

  PropertyDescriptor類表示JavaBean類經過存儲器導出一個屬性。主要方法:
      1. getPropertyType(),得到屬性的Class對象;
      2. getReadMethod(),得到用於讀取屬性值的方法;getWriteMethod(),得到用於寫入屬性值的方法;
      3. hashCode(),獲取對象的哈希值;
      4. setReadMethod(Method readMethod),設置用於讀取屬性值的方法;
      5. setWriteMethod(Method writeMethod),設置用於寫入屬性值的方法。spa

  實例代碼以下:.net

 1 package com.peidasoft.Introspector;
 2 
 3 import java.beans.BeanInfo;
 4 import java.beans.Introspector;
 5 import java.beans.PropertyDescriptor;
 6 import java.lang.reflect.Method;
 7 
 8 public class BeanInfoUtil {  
 9  
10     public static void setProperty(UserInfo userInfo,String userName)throws Exception{
11         PropertyDescriptor propDesc=new PropertyDescriptor(userName,UserInfo.class);
12         Method methodSetUserName=propDesc.getWriteMethod();
13         methodSetUserName.invoke(userInfo, "wong");
14         System.out.println("set userName:"+userInfo.getUserName());
15     }
16   
17     public static void getProperty(UserInfo userInfo,String userName)throws Exception{
18         PropertyDescriptor proDescriptor =new PropertyDescriptor(userName,UserInfo.class);
19         Method methodGetUserName=proDescriptor.getReadMethod();
20         Object objUserName=methodGetUserName.invoke(userInfo);
21         System.out.println("get userName:"+objUserName.toString());
22     }
23 }

Introspector類:code

  將JavaBean中的屬性封裝起來進行操做。在程序把一個類當作JavaBean來看,就是調用Introspector.getBeanInfo()方法,獲得的BeanInfo對象封裝了把這個類當作JavaBean看的結果信息,即屬性的信息。orm

  getPropertyDescriptors(),得到屬性的描述,能夠採用遍歷BeanInfo的方法,來查找、設置類的屬性。具體代碼以下:

 1 package com.peidasoft.Introspector;
 2 
 3 import java.beans.BeanInfo;
 4 import java.beans.Introspector;
 5 import java.beans.PropertyDescriptor;
 6 import java.lang.reflect.Method;
 7 
 8 
 9 public class BeanInfoUtil {
10         
11     public static void setPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
12         BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
13         PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
14         if(proDescrtptors!=null&&proDescrtptors.length>0){
15             for(PropertyDescriptor propDesc:proDescrtptors){
16                 if(propDesc.getName().equals(userName)){
17                     Method methodSetUserName=propDesc.getWriteMethod();
18                     methodSetUserName.invoke(userInfo, "alan");
19                     System.out.println("set userName:"+userInfo.getUserName());
20                     break;
21                 }
22             }
23         }
24     }
25     
26     public static void getPropertyByIntrospector(UserInfo userInfo,String userName)throws Exception{
27         BeanInfo beanInfo=Introspector.getBeanInfo(UserInfo.class);
28         PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
29         if(proDescrtptors!=null&&proDescrtptors.length>0){
30             for(PropertyDescriptor propDesc:proDescrtptors){
31                 if(propDesc.getName().equals(userName)){
32                     Method methodGetUserName=propDesc.getReadMethod();
33                     Object objUserName=methodGetUserName.invoke(userInfo);
34                     System.out.println("get userName:"+objUserName.toString());
35                     break;
36                 }
37             }
38         }
39     }
40     
41 }

  經過這兩個類的比較能夠看出,都是須要得到PropertyDescriptor,只是方式不同:前者經過建立對象直接得到,後者須要遍歷,因此使用PropertyDescriptor類更加方便。

  使用實例:

 1 package com.peidasoft.Introspector;
 2 
 3 public class BeanInfoTest {
 4 
 5     /**
 6      * @param args
 7      */
 8     public static void main(String[] args) {
 9         UserInfo userInfo=new UserInfo();
10         userInfo.setUserName("peida");
11         try {
12             BeanInfoUtil.getProperty(userInfo, "userName");
13             
14             BeanInfoUtil.setProperty(userInfo, "userName");
15             
16             BeanInfoUtil.getProperty(userInfo, "userName");
17             
18             BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName");            
19             
20             BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName");
21             
22             BeanInfoUtil.setProperty(userInfo, "age");
23             
24         } catch (Exception e) {
25             // TODO Auto-generated catch block
26             e.printStackTrace();
27         }
28 
29     }
30 
31 }

輸出:

 1 get userName:peida
 2 set userName:wong
 3 get userName:wong
 4 set userName:alan
 5 get userName:alan
 6 java.lang.IllegalArgumentException: argument type mismatch
 7     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 8     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 9     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
10     at java.lang.reflect.Method.invoke(Method.java:597)
11     at com.peidasoft.Introspector.BeanInfoUtil.setProperty(BeanInfoUtil.java:14)
12     at com.peidasoft.Introspector.BeanInfoTest.main(BeanInfoTest.java:22)

說明:BeanInfoUtil.setProperty(userInfo, "age");報錯是應爲age屬性是int數據類型,而setProperty方法裏面默認給age屬性賦的值是String類型。因此會爆出argument type mismatch參數類型不匹配的錯誤信息。

  BeanUtils工具包:


 

  由上述可看出,內省操做很是的繁瑣,因此因此Apache開發了一套簡單、易用的API來操做Bean的屬性——BeanUtils工具包。
  BeanUtils工具包:下載:http://commons.apache.org/beanutils/ 注意:應用的時候還須要一個logging包 http://commons.apache.org/logging/
  使用BeanUtils工具包完成上面的測試代碼:

 1 package com.peidasoft.Beanutil;
 2 
 3 import java.lang.reflect.InvocationTargetException;
 4 
 5 import org.apache.commons.beanutils.BeanUtils;
 6 import org.apache.commons.beanutils.PropertyUtils;
 7 
 8 import com.peidasoft.Introspector.UserInfo;
 9 
10 public class BeanUtilTest {
11     public static void main(String[] args) {
12         UserInfo userInfo=new UserInfo();
13          try {
14             BeanUtils.setProperty(userInfo, "userName", "peida");
15             
16             System.out.println("set userName:"+userInfo.getUserName());
17             
18             System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName"));
19             
20             BeanUtils.setProperty(userInfo, "age", 18);
21             System.out.println("set age:"+userInfo.getAge());
22             
23             System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age"));
24              
25             System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName());
26             System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName());
27             
28             PropertyUtils.setProperty(userInfo, "age", 8);
29             System.out.println(PropertyUtils.getProperty(userInfo, "age"));
30             
31             System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName());
32                   
33             PropertyUtils.setProperty(userInfo, "age", "8");   
34         } 
35          catch (IllegalAccessException e) {
36             e.printStackTrace();
37         } 
38          catch (InvocationTargetException e) {
39             e.printStackTrace();
40         }
41         catch (NoSuchMethodException e) {
42             e.printStackTrace();
43         }
44     }
45 }

運行結果:

 1 set userName:peida
 2 get userName:peida
 3 set age:18
 4 get age:18
 5 get userName type:java.lang.String
 6 get age type:java.lang.String
 7 java.lang.Integer
 8 Exception in thread "main" java.lang.IllegalArgumentException: Cannot invoke com.peidasoft.Introspector.UserInfo.setAge 
 9 on bean class 'class com.peidasoft.Introspector.UserInfo' - argument type mismatch - had objects of type "java.lang.String" 
10 but expected signature "int"
11     at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2235)
12     at org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:2151)
13     at org.apache.commons.beanutils.PropertyUtilsBean.setNestedProperty(PropertyUtilsBean.java:1957)
14     at org.apache.commons.beanutils.PropertyUtilsBean.setProperty(PropertyUtilsBean.java:2064)
15     at org.apache.commons.beanutils.PropertyUtils.setProperty(PropertyUtils.java:858)
16     at com.peidasoft.orm.Beanutil.BeanUtilTest.main(BeanUtilTest.java:38)
17 Caused by: java.lang.IllegalArgumentException: argument type mismatch
18     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
19     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
20     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
21     at java.lang.reflect.Method.invoke(Method.java:597)
22     at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2170)
23     ... 5 more

說明:

  1.得到屬性的值,例如,BeanUtils.getProperty(userInfo,"userName"),返回字符串
  2.設置屬性的值,例如,BeanUtils.setProperty(userInfo,"age",8),參數是字符串或基本類型自動包裝。設置屬性的值是字符串,得到的值也是字符串,不是基本類型。   3.BeanUtils的特色:
    1). 對基本數據類型的屬性的操做:在WEB開發、使用中,錄入和顯示時,值會被轉換成字符串,但底層運算用的是基本類型,這些類型轉到動做由BeanUtils自動完成。
    2). 對引用數據類型的屬性的操做:首先在類中必須有對象,不能是null,例如,private Date birthday=new Date();。操做的是對象的屬性而不是整個對象,例如,BeanUtils.setProperty(userInfo,"birthday.time",111111);   

 1 package com.peidasoft.Introspector;
 2 import java.util.Date;
 3 
 4 public class UserInfo {
 5 
 6     private Date birthday = new Date();
 7     
 8     public void setBirthday(Date birthday) {
 9         this.birthday = birthday;
10     }
11     public Date getBirthday() {
12         return birthday;
13     }      
14 }
 1 package com.peidasoft.Beanutil;
 2 
 3 import java.lang.reflect.InvocationTargetException;
 4 import org.apache.commons.beanutils.BeanUtils;
 5 import com.peidasoft.Introspector.UserInfo;
 6 
 7 public class BeanUtilTest {
 8     public static void main(String[] args) {
 9         UserInfo userInfo=new UserInfo();
10          try {
11             BeanUtils.setProperty(userInfo, "birthday.time","111111");  
12             Object obj = BeanUtils.getProperty(userInfo, "birthday.time");  
13             System.out.println(obj);          
14         } 
15          catch (IllegalAccessException e) {
16             e.printStackTrace();
17         } 
18          catch (InvocationTargetException e) {
19             e.printStackTrace();
20         }
21         catch (NoSuchMethodException e) {
22             e.printStackTrace();
23         }
24     }
25 }

3.PropertyUtils類和BeanUtils不一樣在於,運行getProperty、setProperty操做時,沒有類型轉換,使用屬性的原有類型或者包裝類。因爲age屬性的數據類型是int,因此方法PropertyUtils.setProperty(userInfo, "age", "8")會爆出數據類型不匹配,沒法將值賦給屬性。

  參考資料:

 


  1.http://www.cnblogs.com/avenwu/archive/2012/02/28/2372586.html

  2.http://blog.csdn.net/zhuruoyun/article/details/8219333

相關文章
相關標籤/搜索