Spring 自定义属性解析器

用例

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns="http://www.springframework.org/schema/beans"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
  6. <property name="propertyEditorRegistrars">
  7. <list>
  8. <bean class="com.huifer.source.spring.bean.DatePropertyRegister"/>
  9. </list>
  10. </property>
  11. <property name="customEditors">
  12. <map>
  13. <entry key="java.util.Date" value="com.huifer.source.spring.bean.DatePropertyEditor">
  14. </entry>
  15. </map>
  16. </property>
  17. </bean>
  18. <bean id="apple" class="com.huifer.source.spring.bean.Apple">
  19. <property name="date" value="2020-01-01 01:01:01"/>
  20. </bean>
  21. </beans>
  1. public class DatePropertyRegister implements PropertyEditorRegistrar {
  2. @Override
  3. public void registerCustomEditors(PropertyEditorRegistry registry) {
  4. registry.registerCustomEditor(Date.class, new CustomDateEditor(
  5. new SimpleDateFormat("yyyy-MM-dd"), true)
  6. );
  7. }
  8. }
  1. public class DatePropertyEditor extends PropertyEditorSupport {
  2. private String format = "yyyy-MM-dd";
  3. public String getFormat() {
  4. return format;
  5. }
  6. public void setFormat(String format) {
  7. this.format = format;
  8. }
  9. @Override
  10. public void setAsText(String text) throws IllegalArgumentException {
  11. System.out.println(text);
  12. SimpleDateFormat sdf = new SimpleDateFormat(format);
  13. try {
  14. Date date = sdf.parse(text);
  15. this.setValue(date);
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }

PropertyEditorRegistrar 解析

  • 直接在DatePropertyRegister打上断点进行查看注册流程

    image-20200117104710142

    直接看调用堆栈获取调用层次

  1. @Override
  2. public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
  3. registerCustomEditor(requiredType, null, propertyEditor);
  4. }
  1. @Override
  2. public void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor) {
  3. if (requiredType == null && propertyPath == null) {
  4. throw new IllegalArgumentException("Either requiredType or propertyPath is required");
  5. }
  6. if (propertyPath != null) {
  7. if (this.customEditorsForPath == null) {
  8. this.customEditorsForPath = new LinkedHashMap<>(16);
  9. }
  10. this.customEditorsForPath.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
  11. }
  12. else {
  13. if (this.customEditors == null) {
  14. this.customEditors = new LinkedHashMap<>(16);
  15. }
  16. // 放入 customEditors map对象中
  17. this.customEditors.put(requiredType, propertyEditor);
  18. this.customEditorCache = null;
  19. }
  20. }
  • PropertyEditorRegistrySupport

    image-20200117111131406

    此处对象是通过DatePropertyRegister传递的

  • org.springframework.beans.factory.support.AbstractBeanFactory#registerCustomEditors

  1. protected void registerCustomEditors(PropertyEditorRegistry registry) {
  2. PropertyEditorRegistrySupport registrySupport =
  3. (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
  4. if (registrySupport != null) {
  5. registrySupport.useConfigValueEditors();
  6. }
  7. if (!this.propertyEditorRegistrars.isEmpty()) {
  8. for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
  9. try {
  10. /**
  11. * {@link ResourceEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)}或者
  12. * {@link PropertyEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)}
  13. */
  14. registrar.registerCustomEditors(registry);
  15. }
  16. catch (BeanCreationException ex) {
  17. Throwable rootCause = ex.getMostSpecificCause();
  18. if (rootCause instanceof BeanCurrentlyInCreationException) {
  19. BeanCreationException bce = (BeanCreationException) rootCause;
  20. String bceBeanName = bce.getBeanName();
  21. if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
  22. if (logger.isDebugEnabled()) {
  23. logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
  24. "] failed because it tried to obtain currently created bean '" +
  25. ex.getBeanName() + "': " + ex.getMessage());
  26. }
  27. onSuppressedException(ex);
  28. continue;
  29. }
  30. }
  31. throw ex;
  32. }
  33. }
  34. }
  35. if (!this.customEditors.isEmpty()) {
  36. this.customEditors.forEach((requiredType, editorClass) ->
  37. registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
  38. }
  39. }
  • void registerCustomEditors(PropertyEditorRegistry registry); 用例中编写的DatePropertyRegister正好有这个方法的实现

·

  • AbstractBeanFactory中查看变量

image-20200117110115741

  • 为什么最后结果变成com.huifer.source.spring.bean.DatePropertyEditor

    看配置文件

    1. <property name="customEditors">
    2. <map>
    3. <entry key="java.util.Date" value="com.huifer.source.spring.bean.DatePropertyEditor">
    4. </entry>
    5. </map>
    6. </property>
    • 对应的 set 方法

      1. public void setCustomEditors(Map<Class<?>, Class<? extends PropertyEditor>> customEditors) {
      2. this.customEditors = customEditors;
      3. }

      image-20200117110846256

applyPropertyValues

  • 应用属性值

    1. protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    2. if (pvs.isEmpty()) {
    3. return;
    4. }
    5. if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
    6. ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
    7. }
    8. MutablePropertyValues mpvs = null;
    9. // 没有解析的属性
    10. List<PropertyValue> original;
    11. if (pvs instanceof MutablePropertyValues) {
    12. mpvs = (MutablePropertyValues) pvs;
    13. if (mpvs.isConverted()) {
    14. //MutablePropertyValues 对象中存在转换后对象直接赋值
    15. // Shortcut: use the pre-converted values as-is.
    16. try {
    17. bw.setPropertyValues(mpvs);
    18. return;
    19. }
    20. catch (BeansException ex) {
    21. throw new BeanCreationException(
    22. mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    23. }
    24. }
    25. original = mpvs.getPropertyValueList();
    26. }
    27. else {
    28. original = Arrays.asList(pvs.getPropertyValues());
    29. }
    30. // 自定义转换器
    31. TypeConverter converter = getCustomTypeConverter();
    32. if (converter == null) {
    33. converter = bw;
    34. }
    35. // 创建BeanDefinitionValueResolver
    36. BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
    37. // Create a deep copy, resolving any references for values.
    38. // 解析后的对象集合
    39. List<PropertyValue> deepCopy = new ArrayList<>(original.size());
    40. boolean resolveNecessary = false;
    41. for (PropertyValue pv : original) {
    42. // 解析过的属性
    43. if (pv.isConverted()) {
    44. deepCopy.add(pv);
    45. }
    46. // 没有解析过的属性
    47. else {
    48. // 属性名称
    49. String propertyName = pv.getName();
    50. // 属性值,直接读取到的
    51. Object originalValue = pv.getValue();
    52. // 解析值
    53. Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
    54. Object convertedValue = resolvedValue;
    55. /**
    56. * 1. isWritableProperty: 属性可写
    57. * 2. isNestedOrIndexedProperty: 是否循环嵌套
    58. */
    59. boolean convertible = bw.isWritableProperty(propertyName) &&
    60. !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
    61. if (convertible) {
    62. // 转换器解析
    63. convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
    64. }
    65. // Possibly store converted value in merged bean definition,
    66. // in order to avoid re-conversion for every created bean instance.
    67. if (resolvedValue == originalValue) {
    68. if (convertible) {
    69. // 设置解析值
    70. pv.setConvertedValue(convertedValue);
    71. }
    72. deepCopy.add(pv);
    73. }
    74. // 类型解析
    75. else if (convertible && originalValue instanceof TypedStringValue &&
    76. !((TypedStringValue) originalValue).isDynamic() &&
    77. !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
    78. pv.setConvertedValue(convertedValue);
    79. deepCopy.add(pv);
    80. }
    81. else {
    82. resolveNecessary = true;
    83. deepCopy.add(new PropertyValue(pv, convertedValue));
    84. }
    85. }
    86. }
    87. if (mpvs != null && !resolveNecessary) {
    88. // 转换成功的标记方法
    89. mpvs.setConverted();
    90. }
    91. // Set our (possibly massaged) deep copy.
    92. try {
    93. bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    94. }
    95. catch (BeansException ex) {
    96. throw new BeanCreationException(
    97. mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    98. }
    99. }

    image-20200117133325461

image-20200117141309038

image-20200117141519123

  • 属性值解析

    image-20200117142800671

    1. @Nullable
    2. private Object convertForProperty(
    3. @Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
    4. if (converter instanceof BeanWrapperImpl) {
    5. return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
    6. }
    7. else {
    8. PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
    9. MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
    10. return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
    11. }
    12. }
  1. private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
  2. try {
  3. editor.setValue(oldValue);
  4. }
  5. catch (Exception ex) {
  6. if (logger.isDebugEnabled()) {
  7. logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
  8. }
  9. // Swallow and proceed.
  10. }
  11. // 调用子类实现方法
  12. editor.setAsText(newTextValue);
  13. return editor.getValue();
  14. }
  • 调用用例编写的方法

    1. @Override
    2. public void setAsText(String text) throws IllegalArgumentException {
    3. System.out.println(text);
    4. SimpleDateFormat sdf = new SimpleDateFormat(format);
    5. try {
    6. Date date = sdf.parse(text);
    7. this.setValue(date);
    8. } catch (Exception e) {
    9. e.printStackTrace();
    10. }
    11. }

image-20200117143022827

该值也是这个方法的返回org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)