一、背景

之前在学习java的时候是从来没有接触过内省的,之所以接触这块内容是由于今天在思考Map与JavaBean之间的相互转换的时候遇到的。
在java中内省(Introspector)是专门用来操作JavaBean属性的。在JavaAPI中有专门的一个类封装了内省的操作,这个类就是Introspector类,通过getBeanInfo(…)方法就可以将某个类中的属性封装到一个BeanInfo类中。

二、JavaBean

并不是任意的一个模型类都是可以称为JavaBean的,需要满足以下的三个条件的才可以称为JavaBean

  • JavaBean类必须是具体的和公共的
  • 具有无参数的构造器
  • 只能有属性和属性的setter和getter方法

下面是JavaBean的一个简单的例子

  1. public class Person {
  2. private String name;
  3. private int age;
  4. public String getName() {
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public int getAge() {
  11. return age;
  12. }
  13. public void setAge(int age) {
  14. this.age = age;
  15. }
  16. public String getAbc() {
  17. return null;
  18. }
  19. }

三、Introspector、BeanInfo、PropertyDescriptor

在JavaAPI中有专门的一个类封装了内省的操作,这个类就是Introspector类,通过getBeanInfo(…)方法就可以将某个类中的属性封装到一个BeanInfo类中。
  取得BeanInfo对象后就相当于取得某个类的所有属性,那么再调用BeanInfo对象中的getPropertyDescriptors()方法获得PropertyDescriptor[]数组对象,每个数组中的元素都是PropertyDescriptor实例(属性描述器),而PropertyDescriptor实例封装了每个属性特有的一些性质,比如调用getReadMethod()方法就能获得这个属性的get方法Method,调用getWriteMethod()方法就能获得这个属性的set方法Method。

  1. @Test
  2. public void testIntro () throws Exception {
  3. Person p = new Person();
  4. BeanInfo beanInfo = Introspector.getBeanInfo(p.getClass());
  5. PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
  6. for(PropertyDescriptor pd:pds) {
  7. System.out.println(pd.getName());
  8. }

输出的结果为:

  1. abc
  2. age
  3. name
  4. class

对输出的结果作以下解释:

1:首先获取的是属性的get方法,在示例的JavaBean中存在的直观的有name和age两个属性 2:abc 属性是getAbc方法的,name属性就是abc 3:最后还有一个从Object类中继承的getClass方法(属性为class)

四、高级用法

高级用法一:
如果我们不想有父类继承的某些get或set方法而继承下来的属性,比如上述的class,那么我们在最开始使用Introspector. getBeanInfo时可以使用对应的参数列表:
java内省 - 图1

测试代码如下:

  1. @Test
  2. public void testIntro () throws Exception {
  3. Person p = new Person();
  4. BeanInfo beanInfo = Introspector.getBeanInfo(p.getClass(),Object.class);
  5. PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
  6. for(PropertyDescriptor pd:pds) {
  7. System.out.println(pd.getName());
  8. }

输出如下:
image.png
从输出的结果看,第二个参数就是不使用父类继承的方法。

高级用法二:
如果想直接操作一个bean的某个具体属性,那么其实我们可以直接使用属性描述器PropertyDescriptor的构造函数:
java内省 - 图3

比如上述的例子中操作age

  1. @Test
  2. public void test4() throws IntrospectionException, InvocationTargetException, IllegalAccessException {
  3. PropertyDescriptor pd = new PropertyDescriptor("age", Person.class);
  4. Person p = new Person();
  5. Method setAgeMethod = pd.getWriteMethod();
  6. setAgeMethod.invoke(p, "23");
  7. Method getAgeMethod = pd.getReadMethod();
  8. System.out.println(getAgeMethod.invoke(p, null));
  9. }