内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name, 那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter/setter法,通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。
一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。下面我们来看一个例子,这个例子把某个对象的所有属性名称和值都打印出来:
Student.java代码清单:
public class Student {
private String name; //字段
rivate String sex;
private int age;
private String email;
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setXxx(){ //写方法
}
public String getXxx(){ //读方法
return "xxx";
}
}
StudentTest.java代码清单:
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 org.junit.Test;
public class StudentTest {
@Test
public void test() throws IntrospectionException,
IllegalArgumentException,IllegalAccessException,
InvocationTargetException {
Student st = new Student();
// 1、通过Introspector类获得Bean对象的 BeanInfo
BeanInfo entity = Introspector.getBeanInfo(Student.class);
// 2、然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor)
PropertyDescriptor pdrs[] = entity.getPropertyDescriptors();
// 3、通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法
for (PropertyDescriptor pd : pdrs) {
// System.out.println(pd.getName());
/*
* System.out.println(pd.getShortDescription());
* System.out.println(pd.getDisplayName());
*/
/*if (pd.getName().equals("age")) { //age是什么类型?
Method md = pd.getWriteMethod();
md.invoke(st, 12);
}
*/
//获取属性的类型
System.out.println(pd.getName()+" "+pd.getPropertyType());
}
//System.out.println(st.getAge());
}
//简便的方法
@Test
public void test1()throws Exception{
Student st = new Student();
//通过构造器 创建 PropertyDescriptor对象
PropertyDescriptor pd = new PropertyDescriptor("age", Student.class);
Method md = pd.getWriteMethod(); //写操作
md.invoke(st, 120);
System.out.println(st.getAge());
md = pd.getReadMethod();
int value = (Integer)md.invoke(st, null); //读操作
System.out.println(value);
}
//apache beanutils工具包
}
Sun公司的内省API过于繁琐,所以Apache组织结合很多实际开发中的应用场景开发了一套简单、易用的API操作Bean的属性——BeanUtils。
Beanutils工具包的常用类:
BeanUtils
PropertyUtils
ConvertUtils.regsiter(Converter convert, Class clazz)
下面通过一个案例来了解BeanUtils类的使用。
Student.java代码清单:
import java.util.Date;
public class Student {
private String name;
private int age;
private Date birthday;
public String getName() { //读方法
return name;
}
public void setName(String name) { //写方法
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;
import org.junit.Test;
public class Demo01 {
/* 采用BeanUtils为Student 的name属性赋值 */
@Test
public void test1() throws Exception {
// 1、加载Class文件
Class cls = Class.forName("cn.csdn.beanutils.Student");
// 2、创建bean对象
Student bean = (Student)cls.newInstance();
// 3、采用BeanUtils对name属性赋值
BeanUtils.setProperty(bean, "name", "xxx");
//String value = BeanUtils.getProperty(bean, "name");
System.out.println(value);
}
/* Beanutils支持基本数据类型的自动转换 */
@Test
public void test2() throws Exception {
// 1、定义class文件
String className = "cn.csdn.beanutils.Student";
// 2、定义操作的属性
String name = "age";
// 3、创建class对象
Class cls = Class.forName(className);
// 4、创建bean对象
Student bean = (Student) cls.newInstance();
// 5、为操作的bean对象的name属性赋值
BeanUtils.setProperty(bean, name, "200");
// 6、执行输出
System.out.println(bean.getAge());
}
@Test
public void test3() throws IllegalAccessException,InvocationTargetException {
Student st = new Student();
BeanUtils.setProperty(st, "name", "redarmy"); // 避免了基本的数据类型转换的问题
System.out.println(st.getName());
}
@Test
public void test4() throws Exception {
Student bean = new Student();
BeanUtils.setProperty(bean, "birthday", new Date());
System.out.println(bean.getBirthday());
}
@Test
public void test5() throws Exception {
Student bean = new Student();
// 自带的转换器
ConvertUtils.register(new DateLocaleConverter(), Date.class);
BeanUtils.setProperty(bean, "birthday", "1997-12-12");
System.out.println(bean.getBirthday());
}
@Test
public void test6() throws Exception {
Student bean = new Student();
//自定义转换器
ConvertUtils.register(new Converter() {
// 转换的类型 //转换的值
public Object convert(Class type, Object value) {
if (value == null) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date dt = null;
try {
dt = sdf.parse((String) value);
} catch (ParseException e) {
throw new ConversionException("日期格式转换有问题....");
}
return dt;
}}, Date.class);
BeanUtils.setProperty(bean, "birthday", "1997-11-12");
System.out.println(bean.getBirthday()); //toString()
}
}
将Java的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面提到的Struts,还有用于处理XML文件的Digester项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互结合方能发挥真正的智能化以及高度可扩展性。