DirectFieldAccessor
它继承自AbstractNestablePropertyAccessor
,所以它肯定也就可以处理级联属性和集合数组值了。(请注意,在Spring4.2之后支持,之前是不支持的~)
// @since 2.0 出现得可比父类`AbstractNestablePropertyAccessor`要早哦~~~注意:父类的构造函数都是protected的 public class DirectFieldAccessor extends AbstractNestablePropertyAccessor { // 缓存着每个字段的处理器FieldPropertyHandler // ReflectionUtils.findField()根据String去找到Field对象的 private final Map<String, FieldPropertyHandler> fieldMap = new HashMap<>(); public DirectFieldAccessor(Object object) { super(object); } // 这个构造器也是protected 的 所以若你想自己指定nestedPath和parent,你可以继承此类~~~ protected DirectFieldAccessor(Object object, String nestedPath, DirectFieldAccessor parent) { super(object, nestedPath, parent); } ... // 实现父类的抽象方法,依旧使用DirectFieldAccessor去处理~~~ @Override protected DirectFieldAccessor newNestedPropertyAccessor(Object object, String nestedPath) { return new DirectFieldAccessor(object, nestedPath, this); } // 字段field属性处理器,使用内部类实现PropertyHandler ~~~ private class FieldPropertyHandler extends PropertyHandler { private final Field field; // 从此处可以看出`DirectFieldAccessor`里的field默认都是可读、可写的~~~~ public FieldPropertyHandler(Field field) { super(field.getType(), true, true); this.field = field; } ... } }
它的功能是直接操作Bean的属性值,而代替使用get/set方法去操作Bean。它的实现原理就是简单的field.get(getWrappedInstance())和field.set(getWrappedInstance(), value)等。
它处理级联属性的大致步骤是:
- 遇上级联属性,先找出canonicalName
- 根据此canonicalName调用其field.get()拿到此字段的值~
- 若不为null(有初始值),那就继续解析此类型,循而往复即可~
PropertyAccessor使用Demo
本文以DirectFieldAccessor为例,介绍属性访问器PropertyAccessor的使用~
注备两个普通的JavaBean。苹果Apple:
@ToString public class Apple { private String color; // 复杂类型 private Size size = new Size(); // 苹果的尺寸。 存在级联 private String[] arrStr = new String[1]; private List<String> listStr = new ArrayList<>(); private Map<Integer, String> map = new HashMap<>(); // 更为复杂的类型 private List<List<String>> listList = new ArrayList<>(); private List<Map<Integer, String>> listMap = new ArrayList<>(); public Apple() { super(); listList.add(new ArrayList<>()); listMap.add(new HashMap<>()); } }
尺寸Size:
@ToString public class Size { private Integer height; private Integer width; }
类Apple
属性丰富,并且统一都没有
提供get/set方法。使用DirectFieldAccessor
直接的属性访问器给其赋值:
public static void main(String[] args) { Apple apple = new Apple(); PropertyAccessor accessor = new DirectFieldAccessor(apple); // 设置普通属性 accessor.setPropertyValue("color", "红色"); // 设置嵌套属性(注意:此处能够正常work是因为有= new Size(), // 否则报错:Value of nested property 'size' is null 下同~) accessor.setPropertyValue("size.height", 10); // 设置集合/数组属性 accessor.setPropertyValue("arrStr[0]", "arrStr"); accessor.setPropertyValue("arrStr[1]", "arrStr1"); // 注意:虽然初始化时初始化过数组了,但是仍以此处的为准 accessor.setPropertyValue("listStr[0]", "listStr"); accessor.setPropertyValue("listStr[0]", "listStr1"); // 如果角标index一样,后面覆盖前面的 // 虽然listStr是String的List,但是反射绕过了泛型 可以set进去,但一get就报错~~~需要注意这一点 //accessor.setPropertyValue("listStr[0]", new Size()); //accessor.setPropertyValue("listStr[1]", 20); //System.out.println(apple.getListStr().get(0)); //Cannot convert value of type 'com.fsx.bean.Size' to required type 'java.lang.String' // 设置Map:key只能是数值才行,否则是不好使的~~~~ //accessor.setPropertyValue("map['aaa']","myValue1"); //Caused by: java.lang.NumberFormatException: For input string: "aaa" accessor.setPropertyValue("map[1]", "myValue2"); // 设置listList这种集合里的集合 accessor.setPropertyValue("listList[0][0]", "listList00"); accessor.setPropertyValue("listList[0][1]", "listList01"); //accessor.setPropertyValue("listList[1][0]","listList10"); //IndexOutOfBoundsException: Index: 1, Size: 1 //accessor.setPropertyValue("listList[1][1]","listList11"); //IndexOutOfBoundsException: Index: 1, Size: 1 // 设置listMap这种集合里面放Map accessor.setPropertyValue("listMap[0][0]", "listMap00"); //accessor.setPropertyValue("listMap[0]['myKey']","listMapkey"); //For input string: "myKey" // =========打印输出 System.out.println(apple); //Apple(color=红色, size=Size(height=10, width=null), arrStr=[arrStr, arrStr1], listStr=[listStr1], map={1=myValue2}, listList=[[listList00, listList01]], listMap=[{0=listMap00}]) }
从结果中是能够看出来的,使用DirectFieldAccessor能够正确完成属性赋值。这使用DirectFieldAccessor作为实现的话有几点使用小细节需要注意:
若是级联属性、集合数组等复杂属性,初始值不能为null
使用它给属性赋值无序提供get、set方法(侧面意思是:它不会走你的get/set方法逻辑)
当然若你希望null值能够被自动初始化也是可以的,请设值:accessor.setAutoGrowNestedPaths(true);这样数组、集合、Map等都会为null时候给你初始化(其它Bean请保证有默认构造函数)
在实际开发中,DirectFieldAccessor使用的场景相对较少,但有个典型应用是Spring-Data-Redis有使用DirectFieldAccessor来获取属性值~~~
若我们开发中只是单纯的想直接获取属性值,不妨可以使用它,形如这样:new DirectFieldAccessor(client).getPropertyValue("redisURI")非常的方便~~~
当设置属性值时,少不了两样东西:
- 属性访问表达式:如listMap[0][0]
- 属性值:
ProperyValue对象就是用来封装这些信息的。如果某个值要给赋值给bean属性,Spring都会把这个值包装成ProperyValue对象。
PropertyTokenHolder的作用是什么?
这个类的作用是对属性访问表达式的细化和归类。比如这句代码:
.setPropertyValue("listMap[0][0]", "listMapValue00");
这句代码的含义是:为Apple的成员变量listMap的第0个元素:即为Map。然后向该Map里放入键值对:0(key)和listMapValue00(value)。所以listMap[0][0]一个属性访问表达式,它在PropertyTokenHolder类里存储如下:
- canonicalName:listMap[0][0]:代表整个属性访问表达式
- actualName:listMap:仅包含最外层的属性名称
- keys:[0, 0]:数组的长度代表索引深度,各元素代表索引值
由于每个部分各有各的作用,所以就事先分解好,包装成对象,避免重复分解。
总结
本文介绍了PropertyAccessor属性访问器,并且以DirectFieldAccessor来直接操作Bean且提供了使用Demo。
通过本文的学习,能给你开辟一条新思路来操作JavaBean,而不仅仅只是通过get/set了,这种思维在业务开发中基本无用,但在框架设计中尤为重要~