Java 内省(Introspector)深入理解
java 内省(introspector)深入理解
一些概念:
内省(introspector) 是java 语言对 javabean 类属性、事件的一种缺省处理方法。
javabean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进javabean中,这种对象称为“值对象”(value object),或“vo”。方法比较少。这些信息储存在类的私有变量中,通过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, 那我们可以通过 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),设置用于写入属性值的方法。
实例代码如下:
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 setproperty(userinfo userinfo,string username)throws exception{ propertydescriptor propdesc=new propertydescriptor(username,userinfo.class); method methodsetusername=propdesc.getwritemethod(); methodsetusername.invoke(userinfo, "wong"); system.out.println("set username:"+userinfo.getusername()); } public static void getproperty(userinfo userinfo,string username)throws exception{ propertydescriptor prodescriptor =new propertydescriptor(username,userinfo.class); method methodgetusername=prodescriptor.getreadmethod(); object objusername=methodgetusername.invoke(userinfo); system.out.println("get username:"+objusername.tostring()); } }
introspector类:
将javabean中的属性封装起来进行操作。在程序把一个类当做javabean来看,就是调用introspector.getbeaninfo()方法,得到的beaninfo对象封装了把这个类当做javabean看的结果信息,即属性的信息。
getpropertydescriptors(),获得属性的描述,可以采用遍历beaninfo的方法,来查找、设置类的属性。具体代码如下:
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; } } } } }
通过这两个类的比较可以看出,都是需要获得propertydescriptor,只是方式不一样:前者通过创建对象直接获得,后者需要遍历,所以使用propertydescriptor类更加方便。
使用实例:
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)
说明: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工具包完成上面的测试代码:
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(); } } }
运行结果:
set username:peida get username:peida set age:18 get age:18 get username type:java.lang.string get age type:java.lang.string 8 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
说明:
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);
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不同在于,运行getproperty、setproperty操作时,没有类型转换,使用属性的原有类型或者包装类。由于age属性的数据类型是int,所以方法propertyutils.setproperty(userinfo, "age", "8")会爆出数据类型不匹配,无法将值赋给属性。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!