深入理解Java:内省(Introspector)

内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺省处理方法。

  JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。

  例如类UserInfo :

 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类:

  PropertyDescriptor类表示JavaBean类通过存储器导出一个属性。主要方法:
      1. getPropertyType(),获得属性的Class对象;
      2. getReadMethod(),获得用于读取属性值的方法;getWriteMethod(),获得用于写入属性值的方法;
      3. hashCode(),获取对象的哈希值;
      4. setReadMethod(Method readMethod),设置用于读取属性值的方法;
      5. setWriteMethod(Method writeMethod),设置用于写入属性值的方法。

  实例代码如下:

 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类:

  将JavaBean中的属性封装起来进行操作。在程序把一个类当做JavaBean来看,就是调用Introspector.getBeanInfo()方法,得到的BeanInfo对象封装了把这个类当做JavaBean看的结果信息,即属性的信息。

  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

posted @ 2015-12-23 09:33  果维  阅读(287)  评论(0编辑  收藏  举报