资讯专栏INFORMATION COLUMN

封装BeanMap以及java的反射和内省

张巨伟 / 3568人阅读

摘要:所以,我实现了一个称为的类来应用反射。现在流行的语言大都支持反射。这组内省主要是针对类进行操作的,能够获取类的属性信息。可以看到,通过的内省机制,解决了的最关键的问题。在封装反射的时候,会充分考虑到各种情况。

BeanMap

学习具体的技术工具的好办法就是些Demo、造轮子。所以,我实现了一个称为BeanMap的类来应用java反射API。

这个BeanMap的功能是将一个Bean包装成Map来使用。对调用者来说,是以操作Map的方式来操作BeanMap,
但是,实际上的数据是存储在被包装的Bean对象中的。

这种思路类似适配器模式,可以让你以Map的接口操作Bean对象。
但又有点像“视图”思想,真正的数据是存储在Bean对象中的,BeanMap只是对它进行操作的“视图”。对BeanMap的所有操作都会反映在后面的Bean对象中。

下面是BeanMap的一个使用例子。

    @Getter
    @Setter
    class Point {
        private Integer x = 2;
        private Integer y = 1;
    }
BeanMap map = new BeanMap(new Point());
map.put("x", 10);
map.get("y");

示例中的Point这个Bean类拥有属性x和y。但是被BeanMap包装后,它就变成了一个拥有键"x"和键"y"的Map了。

那这个BeanMap类有什么实际应用呢?哈哈这只是我为了写反射的DEMO自己设计出来的一个类。

java反射

稍微思考下不难发现,BeanMap实现的关键点在于,
BeanMap接受外部传入的键,这是一个字符串。之后,它得到找到Bean对象中对应的getter和setter,并操作Bean对象。

将这个要求向通用化的方向分析,也即提供一个与函数匹配的字符串,得到对该函数的引用。

通过反射,就能够实现上面的要求。
现在流行的语言大都支持反射。反射能够在运行时得到程序元数据,比如某类的信息。
还能够根据这些元信息来修改程序状态或逻辑。
由于反射是在 运行 时得到的信息,那么支持反射的语言也必然要在程序运行时将这些元信息存放在内存某处。

java语言提供了反射API,这里是官方完整的文档:https://docs.oracle.com/javas... 。
对于反射出来的信息,Java的反射API将其以类的形式包装提供。
Java的反射机制提供了4个类:

Class 类

Constructor 构造器

Field 属性

Method 方法

现在,试图利用反射API,得到一个POJO类的所有属性名称。如下:

    private List names() {
        Method[] methods = bean.getClass().getMethods();
        List result = new ArrayList<>();
        for (Method getter : methods) {
            int mod = getter.getModifiers();
            if (getter.getName().startsWith("get") && !Modifier.isPublic(mod)) {
                String name = getter.getName().substring("key".length());
                name = name.substring(0, 1).toLowerCase() + key.substring(1);
                result.add(name);
            }
        }
        return result;
    }

上述代码的思路很简单,java提供的反射API,能够得到该类的所有方法列表。按照约定,POJO类的属性xxx的getter方法都命名为GetXxx。那么,遍历这个表,找出所有getter,字符串处理下,就得到了所有的属性。

通过调用对象的getClass()方法得到一个Class对象,也即反射出该对象的Class信息。Class::getMethods函数则是反射出该类的所有方法。

Method::getModifiers得到方法的修饰符信息。它是一个整数,我猜测是用位标记各个修饰符的,处理它需要用到位运算。不过可以使用Modifier这个类的工具方法去处理它。

不过,这里是想实现BeanMap。一种思路是,通过对反射出的列表进行一次处理,除了得到每个属性的名称外,还要得到它们的getter和setter。下面是一种粗暴的实现:

    private List fileds() {
        Method[] fields = bean.getClass().getMethods();
        List result = new ArrayList<>();
        for (Method getter : fields) {
            if (getter.getName().startsWith("get") && isVaildModifier(getter.getModifiers())) {
                String setterName = "set" + getter.getName().substring("get".length());
                for (Method setter : fields) {
                    if (setter.getName().equals(setterName) && isVaildModifier(setter.getModifiers())) {
                        result.add(new Item(this.bean, getter, setter));
                    }
                }
            }
        }
        return result;
    }

当然,上面的实现思路缺点很多。
首先,是性能问题。上面的代码虽然能实现功能,但是太暴力了。
虽然一个POJO类的方法顶多几十个,但是考虑到在具体的实践中,这些都是作为较为底层的基础设施,项目中可能会频繁被业务代码调用,因此对其进行性能上的分析是有必要的。

其次,上面这段逻辑考虑的也不全面。上面的逻辑是对公开的getter进行进一步的处理的,那么,如果getter是static的呢?
如果getter被native修饰呢?如果是超类所拥有的属性,那么该如何处理这种情况呢?

java内省API

为了方便使用,java针对反射API进行了封装,提供了一组内省API。
这组内省API主要是针对POJO类进行操作的,能够获取POJO类的属性信息。

那么,有了jdk自带的用于对Bean进行反射的工具后,上面的逻辑既可以简化了:

    private List fileds() throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(List.class);
        return Stream.of(beanInfo.getPropertyDescriptors()).map((pd) -> {
            return new Item(this.bean, pd.getReadMethod(), pd.getWriteMethod());
        }).collect(Collectors.toList());
    }

使用getBeanInfo方法获取某个Bean类的内省信息,这些信息封装在BeanInfo对象中。

PropertyDescriptor是对Bean类中的一个属性的封装,通过它可以获取该属性的名称、getter方法、setter方法等信息。

beanInfo::getPropertyDescriptors获取Bean类的所有的PropertyDescriptor

可以看到,通过java的内省机制,解决了BeanMap的最关键的问题。而且,使用java自带的内省机制比自己通过反射API处理有以下好处:

内省API基于反射API进行的封装,使用更高层次的接口,当然更省心,开发效率更高。

jdk在封装反射API的时候,会充分考虑到各种情况。如考虑到继承这一问题,Introspector::getBeanInfo函数可接收第二个参数,只反射出继承链中该类到第二个参数指定类之间类的属性。

内省API也充分考虑到了性能,其中拥有缓存机制,以提升性能。

最后

解决了最关键的逻辑后,剩下的部分,就是对Map接口进行实现,填充一些封装目的的代码。在这里,我将核心的逻辑放在BeanMapImpl类中,而BeanMap仅仅负责实现Map接口,相关操作转发到BeanMapImpl相应的方法中实现。这样显得程序结构更为清晰:
https://github.com/frapples/j...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71449.html

相关文章

  • Reflection:Java反射机制应用场景

    近期在维护公司项目的时候遇到一个问题,因为实体类中的 set 方法涉及到了业务逻辑,因此在给对象赋值的过程中不能够使用 set 方法,为了实现功能,所以采用了反射的机制给对象属性赋值,借此机会也了解了反射的一些具体用法和使用场景,分以下两点对反射进行分析: 反射的优势和劣势 反射的应用场景 反射的优势和劣势   个人理解,反射机制实际上就是上帝模式,如果说方法的调用是 Java 正确的打开方式...

    浠ラ箍 评论0 收藏0
  • 详叙BeanWrapperPropertyDescriptor

    摘要:关于它的数据转换使用了如下两种机制隶属于规范。这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进中,这种对象称为值对象,或。 每篇一句 千古以来要饭的没有要早饭的,知道为什么吗? 相关阅读 【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、Pro...

    APICloud 评论0 收藏0
  • 徒手撸框架--实现IoC

    摘要:从而能够进一步深入了解框架。至此我们框架开发完成。虽然说阅读源码是了解框架的最终手段。但是框架作为一个生产框架,为了保证通用和稳定,源码必定是高度抽象,且处理大量细节。下一篇文章应该会是徒手撸框架实现。 原文地址:https://www.xilidou.com/2018/... Spring 作为 J2ee 开发事实上的标准,是每个Java开发人员都需要了解的框架。但是Spring 的...

    rottengeek 评论0 收藏0
  • 【小家Spring】Spring IoC是如何使用BeanWrapperJava内省结合起来给Be

    摘要:从层层委托的依赖关系可以看出,的依赖注入给属性赋值是层层委托的最终给了内省机制,这是框架设计精妙处之一。当然分享到你的朋友圈让更多小伙伴看到也是被作者本人许可的若对技术内容感兴趣可以加入群交流高工架构师群。 每篇一句 具备了技术深度,遇到问题可以快速定位并从根本上解决。有了技术深度之后,学习其它技术可以更快,再深入其它技术也就不会害怕 相关阅读 【小家Spring】聊聊Spring中的...

    waruqi 评论0 收藏0

发表评论

0条评论

张巨伟

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<