前言
本篇文章聊聊Spring数据访问、绑定体系中一个非常重要的组成: 属性访问器(PropertyAccessor)。
首先提醒各位,注意此接口和属性解析器(PropertyResolver)是有本质区别的:属性解析器是用来获取配置数据的,详细使用办法可参考:【小家Spring】关于Spring属性处理器PropertyResolver以及应用运行环境Environment的深度分析,强大的StringValueResolver使用和解析
而属性访问器PropertyAccessor接口的作用是存/取Bean对象的属性。为了体现这个接口它的重要性,据我目前了解我此处贴出这么一句话:
- 所有Spring创建的Bean对象都使用该接口存取Bean属性值
PropertyAccessor
它是可以访问命名属性named properties(例如对象的bean属性或对象中的字段)的类的公共接口。大名鼎鼎的BeanWrapper接口也继承自它,它所在包是org.springframework.beans(BeanWrapper也在此包)
// @since 1.1 出现得非常早 public interface PropertyAccessor { // 简单的说就是级联属性的分隔符。 // 比如foo.bar最终会调用getFoo().getBar()两个方法 String NESTED_PROPERTY_SEPARATOR = "."; char NESTED_PROPERTY_SEPARATOR_CHAR = '.'; // 代表角标index的符号 如person.addresses[0] 这样就可以把值放进集合/数组/Map里了 String PROPERTY_KEY_PREFIX = "["; char PROPERTY_KEY_PREFIX_CHAR = '['; String PROPERTY_KEY_SUFFIX = "]"; char PROPERTY_KEY_SUFFIX_CHAR = ']'; // 此属性是否可读。若属性不存在 返回false boolean isReadableProperty(String propertyName); // 此出行是否可写。若属性不存在,返回false boolean isWritableProperty(String propertyName); // 读方法 @Nullable Class<?> getPropertyType(String propertyName) throws BeansException; @Nullable TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException; @Nullable Object getPropertyValue(String propertyName) throws BeansException; // 写方法 void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException; void setPropertyValue(PropertyValue pv) throws BeansException; // 批量设置值 void setPropertyValues(Map<?, ?> map) throws BeansException; // 说明:PropertyValues和PropertyValue关系特别像PropertySources和PropertySource的关系 void setPropertyValues(PropertyValues pvs) throws BeansException; // 可控制是否接受非法的字段、value值扽 ignoreUnknown/ignoreInvalid分别对应非法属性和非法value值的处理策略~ void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) throws BeansException; void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException; }
它的继承树如下:
最终的实现类主要有DirectFieldAccessor和BeanWrapperImpl,本文作为铺垫,着重聊聊DirectFieldAccessor这个访问器实现类~
说明一下:DirectFieldAccessFallbackBeanWrapper它在spring-data-commons这个jar里面,所以若你没有使用spring-data-xxx是木有此实现类的~~~
ConfigurablePropertyAccessor
可配置的PropertyAccessor。它是一个子接口,提供了可配置的能力,并且它还继承了PropertyEditorRegistry、TypeConverter等接口~~~
// @since 2.0 public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter { // 设置一个ConversionService ,用于对value值进行转换 // 它是Spring3.0后推出来替代属性编辑器PropertyEditors的方案~ void setConversionService(@Nullable ConversionService conversionService); @Nullable ConversionService getConversionService(); // 设置在将属性编辑器应用于属性的新值时是**否提取旧属性值**。 void setExtractOldValueForEditor(boolean extractOldValueForEditor); boolean isExtractOldValueForEditor(); // 设置此实例是否应尝试“自动增长”包含null的嵌套路径。 // true:为null的值会自动被填充为一个默认的value值,而不是抛出异常NullValueInNestedPathException void setAutoGrowNestedPaths(boolean autoGrowNestedPaths); boolean isAutoGrowNestedPaths(); }
按照Spring的设计,对此接口提供了一个抽象实现:AbstractPropertyAccessor
AbstractPropertyAccessor
实现了部分父类的接口以及提供一些模版实现~
// @since 2.0 它继承自TypeConverterSupport 相当于实现了TypeConverter以及PropertyEditorRegistry的所有内容 public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor { // 这两个属性上面已经解释了~~~ private boolean extractOldValueForEditor = false; private boolean autoGrowNestedPaths = false; ... // 省略get/set方法 // setPropertyValue是抽象方法~~~ @Override public void setPropertyValue(PropertyValue pv) throws BeansException { setPropertyValue(pv.getName(), pv.getValue()); } @Override public void setPropertyValues(Map<?, ?> map) throws BeansException { setPropertyValues(new MutablePropertyValues(map)); } // MutablePropertyValues和MutablePropertySources特别像,此处就不再介绍了 // 此方法把Map最终包装成了一个MutablePropertyValues,它还有个web子类:ServletRequestParameterPropertyValues @Override public void setPropertyValues(Map<?, ?> map) throws BeansException { setPropertyValues(new MutablePropertyValues(map)); } // 当然也可以直接传入一个PropertyValues 这里传入fasle,表示默认要求属性和value值必须都合法否则抛出异常 @Override public void setPropertyValues(PropertyValues pvs) throws BeansException { setPropertyValues(pvs, false, false); } @Override public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) throws BeansException { setPropertyValues(pvs, ignoreUnknown, false); } // 此抽象类最重要的实现方法~~~ @Override public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException { List<PropertyAccessException> propertyAccessExceptions = null; // 显然绝大多数情况下,都是MutablePropertyValues~~~~ 直接拿即可 List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ? ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues())); // 遍历一个一个执行,批量设置值最终也还是调用的单个的~~~~ // 这里面是否要抛出异常,ignoreUnknown和ignoreInvalid就生效了。分别对应NotWritablePropertyException和NullValueInNestedPathException两个异常 for (PropertyValue pv : propertyValues) { try { setPropertyValue(pv); } catch (NotWritablePropertyException ex) { if (!ignoreUnknown) { throw ex; } // Otherwise, just ignore it and continue... } catch (NullValueInNestedPathException ex) { if (!ignoreInvalid) { throw ex; } // Otherwise, just ignore it and continue... } catch (PropertyAccessException ex) { if (propertyAccessExceptions == null) { propertyAccessExceptions = new ArrayList<>(); } // 把异常收集,因为是for循环,最终一次性抛出 propertyAccessExceptions.add(ex); } } // If we encountered individual exceptions, throw the composite exception. if (propertyAccessExceptions != null) { PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]); throw new PropertyBatchUpdateException(paeArray); } } // 子类AbstractNestablePropertyAccessor重写了此方法 // Redefined with public visibility. @Override @Nullable public Class<?> getPropertyType(String propertyPath) { return null; } // 抽象方法 相当于具体的get/set方法由子类去实现的~~ @Override @Nullable public abstract Object getPropertyValue(String propertyName) throws BeansException; @Override public abstract void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException; }
它主要完成了对PropertyEditorRegistry和TypeConverter等接口的间接实现,然后完成了批量操作的模版操作,但是很明显最终的落地的get/set留给子类来实现~
getPropertyValue和setPropertyValue是分别用于获取和设置bean的属性值的。
AbstractNestablePropertyAccessor
一个典型的实现,为其它所有使用案例提供必要的基础设施。nestable:可嵌套的,支持嵌套的
// @since 4.2 public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyAccessor { private int autoGrowCollectionLimit = Integer.MAX_VALUE; @Nullable Object wrappedObject; private String nestedPath = ""; @Nullable Object rootObject; /** Map with cached nested Accessors: nested path -> Accessor instance. */ @Nullable private Map<String, AbstractNestablePropertyAccessor> nestedPropertyAccessors; // 默认是注册默认的属性编辑器的:defaultEditors 它几乎处理了所有的Java内置类型 包括基本类型、包装类型以及对应数组类型~~~ protected AbstractNestablePropertyAccessor() { this(true); } protected AbstractNestablePropertyAccessor(boolean registerDefaultEditors) { if (registerDefaultEditors) { registerDefaultEditors(); } this.typeConverterDelegate = new TypeConverterDelegate(this); } protected AbstractNestablePropertyAccessor(Object object) { registerDefaultEditors(); setWrappedInstance(object); } protected AbstractNestablePropertyAccessor(Class<?> clazz) { registerDefaultEditors(); // 传的Clazz 那就会反射先创建一个实例对象 setWrappedInstance(BeanUtils.instantiateClass(clazz)); } protected AbstractNestablePropertyAccessor(Object object, String nestedPath, Object rootObject) { registerDefaultEditors(); setWrappedInstance(object, nestedPath, rootObject); } // parent:不能为null protected AbstractNestablePropertyAccessor(Object object, String nestedPath, AbstractNestablePropertyAccessor parent) { setWrappedInstance(object, nestedPath, parent.getWrappedInstance()); setExtractOldValueForEditor(parent.isExtractOldValueForEditor()); setAutoGrowNestedPaths(parent.isAutoGrowNestedPaths()); setAutoGrowCollectionLimit(parent.getAutoGrowCollectionLimit()); setConversionService(parent.getConversionService()); } // wrappedObject:目标对象 public void setWrappedInstance(Object object, @Nullable String nestedPath, @Nullable Object rootObject) { this.wrappedObject = ObjectUtils.unwrapOptional(object); Assert.notNull(this.wrappedObject, "Target object must not be null"); this.nestedPath = (nestedPath != null ? nestedPath : ""); // 此处根对象,若nestedPath存在的话,是可以自定义一个rootObject的~~~ this.rootObject = (!this.nestedPath.isEmpty() ? rootObject : this.wrappedObject); this.nestedPropertyAccessors = null; this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); } public final Object getWrappedInstance() { Assert.state(this.wrappedObject != null, "No wrapped object"); return this.wrappedObject; } public final String getNestedPath() { return this.nestedPath; } // 显然rootObject和NestedPath相关,默认它就是wrappedObject public final Object getRootInstance() { Assert.state(this.rootObject != null, "No root object"); return this.rootObject; } ... // 简单的说,它会处理.逻辑以及[0]等逻辑 [0]对应着集合和数组都可 }
此访问器将集合和数组值转换为相应的目标集合或数组,当然还解决了级联属性(嵌套属性)的问题~
需要特别注意的是:
AbstractNestablePropertyAccessor
这个抽象类在Spring4.2后才提供~~~