概述
1. 定义
- 内省(Introspector)是Java对Bean类属性、事件的一种缺省处理方法。给定一个javabean对象,我们就可以通过内省的方式得到/调用它的所有的get/set方法。
- JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有域,且方法名符合某种命名规则(例如标准的get/set方法)。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(VO, Value Object),方法比较少。这些信息储存在类的私有变量中,通过set/get方法进行操作。
- 例如类A中有属性name, 那我们可以通过getName/setName来得到其值或者设置新的值,通过getName/setName这种方式来访问name字段,就是内省的默认规则。
- Java中提供了一套API用来访问某个属性的getter/setter方法,通过这些API可以使你不需要了解访问JavaBean私有域的方法就可以操作私有域,这些API存放于包java.beans中。
- 换言之,内省机制就是通过属性名来获取某JavaBean类的getter/setter方法,进一步通过该方法进行数据操作的机制。这里的属性名并非类的私有域名,而是取决于具体的getter/setter方法名。
例如对于私有域private String str; 如果对应的getter/setter方法为public String getName(),public void setName(String str)。那么属性名就是“name”,内省机制就是通过“name”获取到上述方法的Method实例,进一步就可以通过反射来操纵数据。
2. 类UML结构
3. 流程
- 通过类 Introspector 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
- 更简单地,可以直接通过 PropertyDescriptor 的构造器,构造某个类的属性描述器,通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
API
1. Introspector类
- 获取指定Bean类的BeanInfo实例
static BeanInfo getBeanInfo(Class<?> beanClass)
static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass, int flags)
static BeanInfo getBeanInfo(Class<?> beanClass, int flags)
2. BeanInfo接口
- 获取该Bean类的方法属性描述器
MethodDescriptor[] getMethodDescriptors()
PropertyDescriptor[] getPropertyDescriptors()
3. MethodDescriptor类
构造器
- 通过方法直接构造方法描述器实例
MethodDescriptor(Method method)
MethodDescriptor(Method method, ParameterDescriptor[] parameterDescriptors)
方法
- 获取该方法描述器所对应的方法
Method getMethod() - 获取该方法描述器所对应的方法参数描述器
ParameterDescriptor[] getParameterDescriptors()
4. PropertyDescriptor类
构造器
- 通过Bean类字节码实例和属性名直接构造属性描述器实例
PropertyDescriptor(String propertyName, Class<?> beanClass)
PropertyDescriptor(String propertyName, Class<?> beanClass, String readMethodName, String writeMethodName)
PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod)
方法
- 通过属性描述器获取该属性的getter/setter方法
Method getReadMethod()
Method getWriteMethod()
示例
Bean.java
package bean;
import java.util.Date;
public class Bean {
private int i;
private Date birth = new Date();
public Bean(int i) {
super();
this.i = i;
}
public int getNum() {
return i;
}
public void setNum(int i) {
this.i = i;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
}
IntrospectorDemo.java
package introspector;
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 bean.Bean;
@SuppressWarnings("unused")
public class IntrospectorDemo {
public static void main(String[] args) throws Exception {
Bean bean = new Bean(13);
String propertyName = "num";
// 通过内省获取该属性值
Object value1 = getProperty1(bean, propertyName); // 13
Object value2 = getProperty2(bean, propertyName); // 13
// 通过内省设置该属性值
setProperty(bean, propertyName, 22);
System.out.println(bean.getNum()); // 22
}
/**
* 通过Introspector获取指定属性描述器。
*
* @param bean
* @param propertyName
* @return
* @throws Exception
*/
private static Object getProperty1(Bean bean, String propertyName) throws Exception {
// 使用Introspector获取Bean类的BeanInfo实例
BeanInfo bi = Introspector.getBeanInfo(bean.getClass());
// 使用BeanInfo获取Bean类的所有属性描述器
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
// 找到指定的属性描述器
if (pd.getName().equals(propertyName)) {
// 获取该属性的get方法,并调用该方法获取属性值。
Method method = pd.getReadMethod();
Object value = method.invoke(bean);
return value;
}
}
return null;
}
/**
* 通过PropertyDescriptor构造器构造实例,直接获取指定属性描述器。
*
* @param bean
* @param propertyName
* @return
* @throws Exception
*/
private static Object getProperty2(Bean bean, String propertyName) throws Exception {
// 使用PropertyDescriptor构造器直接构造指定属性描述器。
PropertyDescriptor pd = new PropertyDescriptor(propertyName, bean.getClass());
// 获取该属性的get方法,并调用该方法获取属性值。
Method method = pd.getReadMethod();
Object value = method.invoke(bean);
return value;
}
/**
* 同样有两种方法设置属性值,这里通过PropertyDescriptor构造器构造实例,直接获取指定属性描述器。
* @param bean
* @param propertyName
* @param i
* @throws Exception
*/
private static void setProperty(Bean bean, String propertyName, int i) throws Exception {
// 使用PropertyDescriptor构造器直接构造指定属性描述器。
PropertyDescriptor pd = new PropertyDescriptor(propertyName, bean.getClass());
// 获取该属性的set方法,并调用该方法获取属性值。
Method method = pd.getWriteMethod();
Object value = method.invoke(bean, i);
}
}
BeanUtils工具包
1. 概述
- Apache提供了一套方便操作JavaBean的工具包:BeanUtils工具包,工具包中的方法大多是静态的。
- BeanUtils类可以方便访问Bean类属性,并以字符串的方式进行处理,支持基本数据类型的转换。
- PropertyUtils类可以方便访问Bean类属性,与BeanUtils不同的是,他是以属性原类型的方式进行处理。
- BeanUtils工具包可以方便处理基本数据类型的属性,若非基本数据类型,则需要ConvetUtils包中的注册转换器进行转换。
- BeanUtils工具包支持属性的级联操作,例如:对Date类型属性中的Time属性进行操作。
- BeanUtils工具包可以方便地将Bean类属性信息与Map进行转换
2. 示例
package introspector;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import bean.Bean;
@SuppressWarnings("unused")
public class BeanUtilsDemo {
public static void main(String[] args) throws Exception {
Bean bean = new Bean(22);
String propertyName = "num";
// 使用BeanUtils获取/设置属性值
String property1 = BeanUtils.getProperty(bean, propertyName); // "22"
BeanUtils.setProperty(bean, propertyName, "88" /*88*/);
System.out.println(bean.getNum()); // 88
// 使用BeanUtils获取/设置Bean中Bean属性值
String property2 = BeanUtils.getProperty(bean, "birth.time"); // 1419949175301
BeanUtils.setProperty(bean, "birth.time", "1000000000000");
System.out.println(bean.getBirth()); // Sun Sep 09 09:46:40 CST 2001
// 使用PropertyUtils获取/设置属性值
Integer num = (Integer) PropertyUtils.getProperty(bean, propertyName); // 88
}
}