Spring 自定义属性解析器

举报
龙哥手记 发表于 2022/11/21 23:13:43 2022/11/21
【摘要】 《读尽源码 第三十篇》

用例

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="propertyEditorRegistrars">
            <list>
                <bean class="com.huifer.source.spring.bean.DatePropertyRegister"/>
            </list>
        </property>

        <property name="customEditors">
            <map>
                <entry key="java.util.Date" value="com.huifer.source.spring.bean.DatePropertyEditor">
                </entry>
            </map>
        </property>
    </bean>
    <bean id="apple" class="com.huifer.source.spring.bean.Apple">
        <property name="date" value="2020-01-01 01:01:01"/>
    </bean>
</beans>Copy to clipboardErrorCopied
public class DatePropertyRegister implements PropertyEditorRegistrar {
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(Date.class, new CustomDateEditor(
                new SimpleDateFormat("yyyy-MM-dd"), true)
        );
    }
}Copy to clipboardErrorCopied
public class DatePropertyEditor extends PropertyEditorSupport {
    private String format = "yyyy-MM-dd";

    public String getFormat() {
        return format;
    }

    public void setFormat(String format) {
        this.format = format;
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        System.out.println(text);
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        try {
            Date date = sdf.parse(text);
            this.setValue(date);
        } catch (Exception e) {
            e.printStackTrace();

        }
    }

}Copy to clipboardErrorCopied

PropertyEditorRegistrar 解析

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

    image-20200117104710142

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

    @Override
    public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
        registerCustomEditor(requiredType, null, propertyEditor);
    }
Copy to clipboardErrorCopied
    @Override
    public void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor) {
        if (requiredType == null && propertyPath == null) {
            throw new IllegalArgumentException("Either requiredType or propertyPath is required");
        }
        if (propertyPath != null) {
            if (this.customEditorsForPath == null) {
                this.customEditorsForPath = new LinkedHashMap<>(16);
            }
            this.customEditorsForPath.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
        }
        else {
            if (this.customEditors == null) {
                this.customEditors = new LinkedHashMap<>(16);
            }
            // 放入 customEditors map对象中
            this.customEditors.put(requiredType, propertyEditor);
            this.customEditorCache = null;
        }
    }
Copy to clipboardErrorCopied
  • PropertyEditorRegistrySupport

    image-20200117111131406

    此处对象是通过DatePropertyRegister传递的

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

    protected void registerCustomEditors(PropertyEditorRegistry registry) {
        PropertyEditorRegistrySupport registrySupport =
                (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
        if (registrySupport != null) {
            registrySupport.useConfigValueEditors();
        }
        if (!this.propertyEditorRegistrars.isEmpty()) {
            for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
                try {
                    /**
                     * {@link ResourceEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)}或者
                     * {@link PropertyEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)}
                     */
                    registrar.registerCustomEditors(registry);
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        String bceBeanName = bce.getBeanName();
                        if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
                                        "] failed because it tried to obtain currently created bean '" +
                                        ex.getBeanName() + "': " + ex.getMessage());
                            }
                            onSuppressedException(ex);
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
        if (!this.customEditors.isEmpty()) {
            this.customEditors.forEach((requiredType, editorClass) ->
                    registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
        }
    }
Copy to clipboardErrorCopied
  • void registerCustomEditors(PropertyEditorRegistry registry); 用例中编写的DatePropertyRegister正好有这个方法的实现

·

  • 在AbstractBeanFactory中查看变量

image-20200117110115741

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

    看配置文件

            <property name="customEditors">
                <map>
                    <entry key="java.util.Date" value="com.huifer.source.spring.bean.DatePropertyEditor">
                    </entry>
                </map>
            </property>
    Copy to clipboardErrorCopied
    • 对应的 set 方法

          public void setCustomEditors(Map<Class<?>, Class<? extends PropertyEditor>> customEditors) {
              this.customEditors = customEditors;
          }Copy to clipboardErrorCopied

      image-20200117110846256

applyPropertyValues

  • 应用属性值

        protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
            if (pvs.isEmpty()) {
                return;
            }
    
            if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
                ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
            }
    
            MutablePropertyValues mpvs = null;
            // 没有解析的属性
            List<PropertyValue> original;
    
            if (pvs instanceof MutablePropertyValues) {
                mpvs = (MutablePropertyValues) pvs;
                if (mpvs.isConverted()) {
                    //MutablePropertyValues 对象中存在转换后对象直接赋值
                    // Shortcut: use the pre-converted values as-is.
                    try {
                        bw.setPropertyValues(mpvs);
                        return;
                    }
                    catch (BeansException ex) {
                        throw new BeanCreationException(
                                mbd.getResourceDescription(), beanName, "Error setting property values", ex);
                    }
                }
                original = mpvs.getPropertyValueList();
            }
            else {
                original = Arrays.asList(pvs.getPropertyValues());
            }
            // 自定义转换器
            TypeConverter converter = getCustomTypeConverter();
            if (converter == null) {
                converter = bw;
            }
            //  创建BeanDefinitionValueResolver
            BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
    
            // Create a deep copy, resolving any references for values.
            // 解析后的对象集合
            List<PropertyValue> deepCopy = new ArrayList<>(original.size());
            boolean resolveNecessary = false;
            for (PropertyValue pv : original) {
                // 解析过的属性
                if (pv.isConverted()) {
                    deepCopy.add(pv);
                }
                // 没有解析过的属性
                else {
                    // 属性名称
                    String propertyName = pv.getName();
                    // 属性值,直接读取到的
                    Object originalValue = pv.getValue();
                    // 解析值
                    Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                    Object convertedValue = resolvedValue;
                    /**
                     * 1. isWritableProperty: 属性可写
                     * 2. isNestedOrIndexedProperty: 是否循环嵌套
                     */
                    boolean convertible = bw.isWritableProperty(propertyName) &&
                            !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                    if (convertible) {
                        // 转换器解析
                        convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                    }
                    // Possibly store converted value in merged bean definition,
                    // in order to avoid re-conversion for every created bean instance.
                    if (resolvedValue == originalValue) {
                        if (convertible) {
                            // 设置解析值
                            pv.setConvertedValue(convertedValue);
                        }
                        deepCopy.add(pv);
                    }
                    // 类型解析
                    else if (convertible && originalValue instanceof TypedStringValue &&
                            !((TypedStringValue) originalValue).isDynamic() &&
                            !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                        pv.setConvertedValue(convertedValue);
                        deepCopy.add(pv);
                    }
                    else {
                        resolveNecessary = true;
                        deepCopy.add(new PropertyValue(pv, convertedValue));
                    }
                }
            }
            if (mpvs != null && !resolveNecessary) {
                // 转换成功的标记方法
                mpvs.setConverted();
            }
    
            // Set our (possibly massaged) deep copy.
            try {
                bw.setPropertyValues(new MutablePropertyValues(deepCopy));
            }
            catch (BeansException ex) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Error setting property values", ex);
            }
        }
    Copy to clipboardErrorCopied

    image-20200117133325461

image-20200117141309038

image-20200117141519123

  • 属性值解析

    image-20200117142800671

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

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            System.out.println(text);
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            try {
                Date date = sdf.parse(text);
                this.setValue(date);
            } catch (Exception e) {
                e.printStackTrace();
    
            }
        }
    Copy to clipboardErrorCopied

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)

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。