学习具体的技术工具的好办法就是些Demo、造轮子。因此,我实现了一个称为BeanMap的类来应用java反射API。java
这个BeanMap的功能是将一个Bean包装成Map来使用。对调用者来讲,是以操做Map的方式来操做BeanMap,
可是,实际上的数据是存储在被包装的Bean对象中的。git
这种思路相似适配器模式,可让你以Map的接口操做Bean对象。
但又有点像“视图”思想,真正的数据是存储在Bean对象中的,BeanMap只是对它进行操做的“视图”。对BeanMap的全部操做都会反映在后面的Bean对象中。github
下面是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了。oracle
那这个BeanMap类有什么实际应用呢?哈哈这只是我为了写反射的DEMO本身设计出来的一个类。 app
稍微思考下不难发现,BeanMap实现的关键点在于,
BeanMap接受外部传入的键,这是一个字符串。以后,它获得找到Bean对象中对应的getter和setter,并操做Bean对象。函数
将这个要求向通用化的方向分析,也即提供一个与函数匹配的字符串,获得对该函数的引用。工具
经过反射,就可以实现上面的要求。
如今流行的语言大都支持反射。反射可以在运行时获得程序元数据,好比某类的信息。
还可以根据这些元信息来修改程序状态或逻辑。
因为反射是在 运行 时获得的信息,那么支持反射的语言也必然要在程序运行时将这些元信息存放在内存某处。性能
java语言提供了反射API,这里是官方完整的文档:https://docs.oracle.com/javas... 。
对于反射出来的信息,Java的反射API将其以类的形式包装提供。
Java的反射机制提供了4个类:学习
如今,试图利用反射API,获得一个POJO类的全部属性名称。以下:
private List<String> names() { Method[] methods = bean.getClass().getMethods(); List<String> 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; }
不过,这里是想实现BeanMap。一种思路是,经过对反射出的列表进行一次处理,除了获得每一个属性的名称外,还要获得它们的getter和setter。下面是一种粗暴的实现:
private List<Item> fileds() { Method[] fields = bean.getClass().getMethods(); List<Item> 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进行了封装,提供了一组内省API。
这组内省API主要是针对POJO类进行操做的,可以获取POJO类的属性信息。
那么,有了jdk自带的用于对Bean进行反射的工具后,上面的逻辑既能够简化了:
private List<Item> 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处理有如下好处:
解决了最关键的逻辑后,剩下的部分,就是对Map接口进行实现,填充一些封装目的的代码。在这里,我将核心的逻辑放在BeanMapImpl类中,而BeanMap仅仅负责实现Map接口,相关操做转发到BeanMapImpl相应的方法中实现。这样显得程序结构更为清晰:
https://github.com/frapples/j...