关于JAVA中的内省机制,你了解多少?

565 阅读2分钟

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

1、什么是内省

内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺省处理方法。

JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。

它的底层实现还是Java的反射。

2、JDK内省类库

2.1、Introspector类

将JavaBean中的属性封装起来进行操作。程序把一个类当做JavaBean来看,就是调用Introspector.getBeanInfo()方法

@Test
public void test1() throws IntrospectionException {
    //获取 User Bean 信息
    BeanInfo userBeanInfo = Introspector.getBeanInfo(User.class);
    //属性描述
    PropertyDescriptor[] propertyDescriptors = userBeanInfo.getPropertyDescriptors();
    System.out.println("属性描述:");
    Stream.of(propertyDescriptors).forEach(System.out::println);
    //方法描述
    System.out.println("方法描述:");
    MethodDescriptor[] methodDescriptors = userBeanInfo.getMethodDescriptors();
    Stream.of(methodDescriptors).forEach(System.out::println);
    //事件描述
    System.out.println("事件描述:");
    EventSetDescriptor[] eventSetDescriptors = userBeanInfo.getEventSetDescriptors();
    Stream.of(eventSetDescriptors).forEach(System.out::println);
}

2.2、PropertyDescriptor类

PropertyDescriptor类表示JavaBean类通过存储器导出一个属性。主要方法:

  1. getPropertyType(),获得属性的Class对象;
  2. getReadMethod(),获得用于读取属性值的方法;getWriteMethod(),获得用于写入属性值的方法;
  3. hashCode(),获取对象的哈希值;
  4. setReadMethod(Method readMethod),设置用于读取属性值的方法;
  5. setWriteMethod(Method writeMethod),设置用于写入属性值的方法。

2.3、属性变化类

java.beans 包中也提供了相应实现:

  • PropertyChangeSupport
  • VetoableChangeSupport 支持约束属性的bean使用的实用程序类,比如判断新值不为空

2.4、代码实战

假设有类UserInfo:

public class UserInfo {

    private String userName;

    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}

新建了一个测试类,分别使用PropertyDescriptorIntrospector、还有Spring对内省的封装类BeanWrapper

测试类如下:

public class IntrospectorTest {
    @Test
    public void test() throws Exception {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("zhang san");

        // 使用PropertyDescriptor
        setProperty(userInfo, "userName");

        // 使用Introspector
        setPropertyUseIntrospector(userInfo, "userName");

        // 使用Spring
        setPropertyUseSpring(userInfo, "userName");

    }

    public static void setPropertyUseSpring(UserInfo userInfo, String userName) {
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(userInfo);
        MutablePropertyValues pvs = new MutablePropertyValues();
        pvs.add(userName,"使用spring");
        bw.setPropertyValues(pvs);
        System.out.println("set userName:" + userInfo.getUserName());
    }


    public static void setProperty(UserInfo userInfo, String userName) throws Exception {
        PropertyDescriptor propDesc = new PropertyDescriptor(userName, UserInfo.class);
        Method methodSetUserName = propDesc.getWriteMethod();
        methodSetUserName.invoke(userInfo, "使用PropertyDescriptor");
        System.out.println("set userName:" + userInfo.getUserName());
    }


    public static void setPropertyUseIntrospector(UserInfo userInfo, String userName) throws Exception {
        BeanInfo beanInfo= Introspector.getBeanInfo(UserInfo.class);
        Arrays.stream(beanInfo.getPropertyDescriptors())
                .filter(x->x.getName().equals(userName))
                .findFirst()
                .get()
                .getWriteMethod().invoke(userInfo,"使用Introspector");
        System.out.println("set userName:" + userInfo.getUserName());
    }
}

输出为:

set userName:使用PropertyDescriptor
set userName:使用Introspector
set userName:使用spring

Java中通过java.beans.Introspector来进行内省操作。常用的内省操作主要有下面这些,当然还有其它的附加类型

图片

3、总结

Java反射是在运行时获取一个类的所有信息,可以操纵类的字段、方法、构造器等,功能非常强大。而内省其实就是反射的一个子集,基于反射实现。专门操作JavaBean的,只关注于JavaBean的属性、方法、事件的一些属性。