Java内省详解
内省和反射有什么区别:
反射式在运行状态把Java类中的各种成分映射成相应的Java类,可以动态的获取所有的属性以及动态调用任意一个方法,强调的是运行状态。
内省机制是通过反射来实现的,BeanInfo用来暴露一个bean的属性、方法和事件,以后我们就可以操纵该JavaBean的属性。
1.通过PropertyDescriptor修改属性方式
1 public class BeanInfoUtil { 2 public static void setProperty(UserInfo userInfo,String userName)throws Exception{ 3 PropertyDescriptor propDesc=new PropertyDescriptor(userName,UserInfo.class); 4 Method methodSetUserName=propDesc.getWriteMethod(); 5 methodSetUserName.invoke(userInfo, "wong"); 6 System.out.println("set userName:"+userInfo.getUserName()); 7 } 8 9 public static void getProperty(UserInfo userInfo,String userName)throws Exception{ 10 PropertyDescriptor proDescriptor =new PropertyDescriptor(userName,UserInfo.class); 11 Method methodGetUserName=proDescriptor.getReadMethod(); 12 Object objUserName=methodGetUserName.invoke(userInfo); 13 System.out.println("get userName:"+objUserName.toString()); 14 } 15 16 }
2.通过Introspector类修改属性
1 public class BeanInfoUtil2 { 2 public static void setPropertyByIntrospector(UserInfo userInfo, 3 String userName) throws Exception { 4 5 BeanInfo beanInfo = Introspector.getBeanInfo(UserInfo.class); 6 PropertyDescriptor[] proDescrtptors = beanInfo.getPropertyDescriptors(); 7 if (proDescrtptors != null && proDescrtptors.length > 0) { 8 for (PropertyDescriptor propDesc : proDescrtptors) { 9 if (propDesc.getName().equals(userName)) { 10 Method methodSetUserName = propDesc.getWriteMethod(); 11 methodSetUserName.invoke(userInfo, "alan"); 12 System.out 13 .println("set userName:" + userInfo.getUserName()); 14 break; 15 } 16 } 17 } 18 } 19 20 public static void getPropertyByIntrospector(UserInfo userInfo, 21 String userName) throws Exception { 22 BeanInfo beanInfo = Introspector.getBeanInfo(UserInfo.class); 23 PropertyDescriptor[] proDescrtptors = beanInfo.getPropertyDescriptors(); 24 if (proDescrtptors != null && proDescrtptors.length > 0) { 25 for (PropertyDescriptor propDesc : proDescrtptors) { 26 if (propDesc.getName().equals(userName)) { 27 Method methodGetUserName = propDesc.getReadMethod(); 28 Object objUserName = methodGetUserName.invoke(userInfo); 29 System.out 30 .println("get userName:" + objUserName.toString()); 31 break; 32 } 33 } 34 } 35 } 36 37 }
注意事项,在上述修改JavaBean属性的时候,如果数据类型不对的话,会报错。例如BeanInfoUtil.setProperty(userInfo, “age”);报错是应为age属性是int数据类型,而setProperty方法里面默认给age属性赋的值是String类型。所以会爆出argument type mismatch参数类型不匹配的错误信息。
为了解决上述问题,Apache开发了一套简单、易用的API来操作Bean的属性——BeanUtils工具包。
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属性的级联操作;
1 public static void main(String[] args) throws Exception { 2 Point point = new Point(2, 5);//在point中加一个属性 private Date birth = new Date();并产生setter/getter方法 3 String proName = "birth"; 4 Date date= new Date(); 5 date.setTime(10000); 6 BeanUtils.setProperty(point, proName, date); 7 System.out.println(BeanUtils.getProperty(point, proName)); 8 9 BeanUtils.setProperty(point, "birth.time", 10000); 10 System.out.println(BeanUtils.getProperty(point, "birth.time"));//10000 11 } 12 //之所以可以 BeanUtils.setProperty(point, "birth.time", 10000);这样写,那是因为Date类中有getTime()和setTime()方法,即Date类中相当于有time这个属性。
BeanUtils和PropertyUtils对比:
1 public static void main(String[] args) throws Exception { 2 Point point = new Point(2, 5); 3 String proName = "x"; 4 BeanUtils.setProperty(point, proName, "8"); 5 System.out.println(BeanUtils.getProperty(point, proName));//8 6 System.out.println(BeanUtils.getProperty(point, proName).getClass().getName());//java.lang.String 7 8 // PropertyUtils.setProperty(point, proName, "8");//exception:argument type mismatch 9 PropertyUtils.setProperty(point, proName, 8); 10 System.out.println(PropertyUtils.getProperty(point, proName));//8 11 System.out.println(PropertyUtils.getProperty(point, proName).getClass().getName());//java.lang.Integer 12 } 13 //BeanUtils它以字符串的形式对javabean进行转换,而PropertyUtils是以原本的类型对javabean进行操作。如果类型不对,就会有argument type mismatch异常。
理解了相应的原理,那些现成的工具用起来就会更舒服,如Beanutils与 PropertyUtils工具。这两个工具设置属性的时候一个主要区别是PropertyUtils.getPropety方法获得的属性值的类型为该 属性本来的类型,而BeanUtils.getProperty则是将该属性的值转换成字符串后才返回。