深入学习java源码之Introspector.getBeanInfo()与BeanInfo类的getPropertyDescriptors()方法
目录
- 深入学习java源码之Introspector.getBeanInfo()与BeanInfo类的getPropertyDescriptors()方法
- 内省
- 1.如何操作JavaBean的属性
- 2.内省概述
- 3.内省怎么用
- 1).操作一个属性
- 2).操作多个属性
- 代码实操案例
- 应用场景
- 总结
内省
1.如何操作JavaBean的属性
通过反射的方式操作JavaBean的属性,jdk提供了PropertyDescription类来操作访问JavaBean的属性,Beantils工具基于此来实现。
2.内省概述
内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其 值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的 getter/setter方法,通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。
一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器 (PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来 调用这些方法。下面我们来看一个例子,这个例子把某个对象的所有属性名称和值都打印出来:
3.内省怎么用
1).操作一个属性
Object obj = new Object();
PropertyDescriptor pd = new PropertyDescriptor(propertyName,Class); //声明属性描述对象,一次只可描述一个属性
Method m = pd.getWriterMethod();//获取setter方法
m.invoke(obj,value);
Method m = pd.getReaderMethod();//获取getter方法
Object value = m.invoke(obj);
2).操作多个属性
BeanInfo bi = Instospector.getBeanInfo(beanClass);//获取Bean描述对象
PropertyDescriptor[] pds = bi.getPropertyDescriptors();//获取属性描述对象数组
拿到属性描述对象数组之后再循环数组,剩余的操作就跟"操作一个属性"相同了。
代码实操案例
bean包下的User类
package Introspector_getBeanInfo.bean;
public class User {
private int id;
private String name;
private String sex;
private int age;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User() {
}
public User(int id, String name, String sex, int age, String address) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
代码案例:将Bean转成Map
package Introspector_getBeanInfo;
import Introspector_getBeanInfo.bean.User;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class IntrospectorDemo01 {
public static Map convertBeanToMap(Object bean) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
Map returnMap = new HashMap();
//获得对象的名字
Class beanClass = bean.getClass();
System.out.println("beanClass="+beanClass);
//在JavaBean上行进内省,了解其所有属性、公开的方法和事件
BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
System.out.println("beanInfo="+beanInfo);
//获得beans PropertyDesciptors
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (int i = 0; i < propertyDescriptors.length; i++) {
PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
System.out.println("propertyDescriptor="+propertyDescriptor);
//获得所有对象属性值的名字
String propertyName = propertyDescriptor.getName();
System.out.println("propertyName="+propertyName);
//如果属性名不等于class
if(!propertyName.equals("class")){
Method readMethod = propertyDescriptor.getReadMethod();
System.out.println("readMethod="+readMethod);
Object propertyValue = readMethod.invoke(bean, new Object[0]);
if (propertyValue!=null){
returnMap.put(propertyName,propertyValue);
} else {
returnMap.put(propertyName,"");
}
}
}
System.out.println();
return returnMap;
}
public static void main(String[] args) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
User user = new User(1,"hhh","男",23,"四川成都");
Map map = IntrospectorDemo01.convertBeanToMap(user);
System.out.println(map);
System.out.println("address="+map.get("address"));
System.out.println("sex="+map.get("sex"));
System.out.println("name="+map.get("name"));
System.out.println("id="+map.get("id"));
System.out.println("age="+map.get("age"));
}
}
案例2:propertyDescriptors[i].getReadMethod();就是获取Bean的getter方法
package Introspector_getBeanInfo;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntrospectorDemo02 {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
IntrospectorDemo02 introspectorDemo02 = new IntrospectorDemo02();
introspectorDemo02.setName("hhh");
// 如果不想把父类的属性也列出来的话,那getBeanInfo的第二个参数填写父类的信息
BeanInfo beanInfo = Introspector.getBeanInfo(IntrospectorDemo02.class, Object.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (int i = 0; i < propertyDescriptors.length; i++) {
String propertyName = propertyDescriptors[i].getName();
Method readMethod = propertyDescriptors[i].getReadMethod();
System.out.println(propertyName+"="+
readMethod.invoke(introspectorDemo02,null));
}
}
}
bean包下的Bean类
package Introspector_getBeanInfo.bean;
public class Bean {
private String propertyName;
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
@Override
public String toString() {
return "Bean{" +
"propertyName='" + propertyName + '\'' +
'}';
}
}
案例3:propertyDescriptors[i].getWriteMethod();获取Bean的setter方法
package Introspector_getBeanInfo;
import Introspector_getBeanInfo.bean.Bean;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntrospectorDemo03 {
public static void main(String[] args) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
Bean bean = new Bean();
// bean.setPropertyName("propertyValue");
// 如果不想把父类的属性也列出来的话,那getBeanInfo的第二个参数填写父类的信息
BeanInfo beanInfo = Introspector.getBeanInfo(Bean.class, Object.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (int i = 0; i < propertyDescriptors.length; i++) {
String propertyName = propertyDescriptors[i].getName();
System.out.println("属性名="+propertyName);
Method writeMethod = propertyDescriptors[i].getWriteMethod();
System.out.println("writeMethod="+writeMethod);
writeMethod.invoke(bean, "propertyValue");
Method readMethod = propertyDescriptors[i].getReadMethod();
System.out.println("readMethod="+readMethod);
Object propertyValue = readMethod.invoke(bean, null);
System.out.println("属性值="+propertyValue);
System.out.println();
System.out.println(propertyName+"="+ propertyValue);
}
}
}
应用场景
Web开发框架Struts中的FormBean就是通过内省机制来将表单中的数据映射到类的属性上,因此要求FormBean的每个属性要有 getter/setter方法。但也并不总是这样,什么意思呢?就是说对一个Bean类来讲,我可以没有属性,但是只要有getter/setter方 法中的其中一个,那么Java的内省机制就会认为存在一个属性,比如类中有方法setMobile,那么就认为存在一个mobile的属性,这样可以方便 我们把Bean类通过一个接口来定义而不用去关心具体实现,不用去关心Bean中数据的存储。比如我们可以把所有的getter/setter方法放到接 口里定义,但是真正数据的存取则是在具体类中去实现,这样可提高系统的扩展性。
总结
将Java的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面 提到的Struts,还有用于处理XML文件的Digester项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互 结合方能发挥真正的智能化以及高度可扩展性。