比反射更好用的内省,你知道吗?

开发 前端
内省机制通过反射获取属性描述器(PropertyDescriptor),然后可以方便地获取和设置属性值。内省操作只针对JavaBean,只有符合JavaBean规则的类的成员才可以采用内省API进行操作。

内省是基于反射实现的,主要用于操作符合JavaBean规范的类。

JavaBean是一种特殊的Java类,通常用于封装多个属性为一个单一的对象。我们再复习下JavaBean规范:

  • 必须要有一个公共无参构造;
  • Javabean类不应有公共属性,属性都应该是 private;
  • 为私有(private声明)属性提供符合命名规范的getter/setter方法;
  • 应该要实现Serializeable接口(内省并不强要求这一条);
  • javaBean类必须是一个公共类,将其访问属性设置为public。

内省机制通过反射获取属性描述器(PropertyDescriptor),然后可以方便地获取和设置属性值。

内省操作只针对JavaBean,只有符合JavaBean规则的类的成员才可以采用内省API进行操作。

内省的核心类在java.beans包下,主要类如下:

java.beans包java.beans包

初识内省

内省就是转为JavaBean准备的,主要包括下面几个类:

  1. Introspector类:这是内省API的核心类,提供了获取BeanInfo对象的方法,例如Introspector.getBeanInfo()方法。
  2. BeanInfo类:这个类包含了关于一个对象的所有Bean属性信息,包括属性的描述符(PropertyDescriptor)。
  3. PropertyDescriptor类:表示一个JavaBean属性的信息,包括getter和setter方法。

以下是一个简单的示例,我们首先获取User的JavaBean信息BeanInfo,然后找到属性描述符PropertyDescriptor列表。

首先定义一个JavaBean:

@Data
public static class User {
    private String username;
}

这里借助Lombok的@Data注解,生成标准JavaBean。

首先获取BeanInfo实例:

final BeanInfo beanInfo = Introspector.getBeanInfo(User.class);

此时beanInfo中就可以获取User类的所有定义:

  • 通过getMethodDescriptors()可以获取方法描述;
  • 通过getPropertyDescriptors()可以获取属性描述;
  • 使用PropertyDescriptor可以生成指定属性的描述;

少年,至此你已经学会屠龙技,可以开始屠龙了。

来个小李子

小李子小李子

话说小李子年轻时候真帅,可惜岁月是把杀猪刀~~

咳咳,言归正传,我们用上面学到的直接开始:

final BeanInfo userBeanInfo = Introspector.getBeanInfo(User.class);
final User user = new User();

for (MethodDescriptor methodDescriptor : userBeanInfo.getMethodDescriptors()) {
    System.out.println("Method Name: " + methodDescriptor.getName());
    final ParameterDescriptor[] parameterDescriptors = methodDescriptor.getParameterDescriptors();
    if (parameterDescriptors == null) {
        continue;
    }
    for (ParameterDescriptor parameterDescriptor : parameterDescriptors) {
        System.out.println("Parameter Name: " + parameterDescriptor.getName());
        System.out.println("Parameter DisplayName: " + parameterDescriptor.getDisplayName());
    }
}

// 遍历所有属性描述符
for (PropertyDescriptor prop : userBeanInfo.getPropertyDescriptors()) {
    System.out.println("Property Name: " + prop.getName());

    // 获取getter方法
    final Method readMethod = prop.getReadMethod();
    if (readMethod != null) {
        Object value = readMethod.invoke(user);
        System.out.println("Property Value: " + value);
    }

    if ("username".equals(prop.getName())) {
        // 获取setter方法
        final Method writeMethod = prop.getWriteMethod();
        if (writeMethod != null) {
            Object value = writeMethod.invoke(user, "看山");
            System.out.println("Property Value: " + value);
        }
    }
}
System.out.println(user);

输出结果是:

Method Name: getClass
Method Name: wait
Method Name: notifyAll
Method Name: notify
Method Name: wait
Method Name: getUsername
Method Name: hashCode
Method Name: setUsername
Method Name: wait
Method Name: equals
Method Name: toString
Property Name: class
Property Value: class cn.howardliu.tutorials.core.reflect.IntrospectorDemo$User
Property Name: username
Property Value: null
Property Value: null
IntrospectorDemo.User(username=看山)

我们可以看到,MethodDescriptor方法描述符包含了User类的所有方法,包括父类的方法。

如果不是JavaBean呢?

我们再看一下,如果User不符合规范呢?

public static class UserNoSetter {
    private String username;

    public void username(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "UserNoSetter{" +
                "username='" + username + '\'' +
                '}';
    }
}

我们再看看上面的结果:

Method Name: getClass
Method Name: wait
Method Name: notifyAll
Method Name: notify
Method Name: wait
Method Name: hashCode
Method Name: setUsername
Method Name: wait
Method Name: equals
Method Name: toString
Method Name: username
Property Name: class
Property Value: class cn.howardliu.tutorials.core.reflect.IntrospectorDemo$UserNoSetter
UserNoSetter{username='null'}

可以看到,方法除了自定义的username和setUsername,其他都是相同的,但是setUsername我们定义成了builder模式,所以内省描述符也不认识了。

责任编辑:武晓燕 来源: 看山的小屋
相关推荐

2023-12-12 08:41:01

2022-09-29 15:32:58

云计算计算模式

2021-10-14 06:52:47

算法校验码结构

2024-09-18 07:00:00

消息队列中间件消息队列

2024-05-28 09:12:10

2024-04-07 00:00:00

ESlint命令变量

2022-03-10 08:25:27

JavaScrip变量作用域

2019-12-12 09:23:29

Hello World操作系统函数库

2023-12-20 08:23:53

NIO组件非阻塞

2024-04-30 09:02:48

2023-04-26 10:21:04

2021-10-28 16:19:37

物联网人工智能IoT

2024-06-03 14:27:08

ThisAPIThat

2024-10-15 11:37:06

2024-06-20 08:06:30

2014-05-30 10:23:15

乐跑手环智能手环运动手环

2020-10-08 18:58:46

条件变量开发线程

2022-12-02 14:12:52

新能源汽车海尔

2022-11-04 14:16:05

2023-03-21 07:39:51

CentOS挂载硬盘
点赞
收藏

51CTO技术栈公众号