Spring IOC源碼解析(07)PropertyEditorRegistrySupport

前言

在講解PropertyEditorRegistrySupport之前,我們有比較對jdk內(nèi)部自帶的PropertyEditor進行說明。

PropertyEditor,意為屬性編輯器,是JavaBean規(guī)范中定義的接口,最初的目的是為IDE的設(shè)計開發(fā)做準(zhǔn)備的雪侥,它可以很方便地讓IDE以可視化的方式設(shè)置JavaBean屬性。但是在Spring中精绎,尤其是IOC的xml配置速缨,我們往往需要將一個字符串靈活地轉(zhuǎn)換為各種其他類型。因此代乃,Spring借鑒了PropertyEditor接口旬牲,在其基礎(chǔ)上做了增強和擴展,同時自帶了一些標(biāo)準(zhǔn)的實現(xiàn)襟己,讓我們可以比較快速而靈活地進行使用引谜。

PropertyEditor

PropertyEditor是屬性編輯器的接口,其主要方法如下:

// 設(shè)置屬性值
void setValue(Object value);

// 獲取屬性值
Object getValue();

// 獲取屬性的初始值字符串
String getJavaInitializationString();

// 將數(shù)值已一個字符串表示
String getAsText();

// 用一個字符串去更新屬性值
void setAsText(String text) throws java.lang.IllegalArgumentException;

// 返回有效屬性值的可羅列值擎浴,例如boolean的有效值為true和false员咽。缺省為null,即:不可羅列的贮预。
String[] getTags();

// 其他...

PropertyEditorSupport

PropertyEditorSupportPropertyEditor的標(biāo)準(zhǔn)實現(xiàn)贝室,大部分邏輯都在此類里面契讲。

public class PropertyEditorSupport implements PropertyEditor {

    /**
     * 構(gòu)造一個對象,源為自己
     * Constructs a <code>PropertyEditorSupport</code> object.
     *
     * @since 1.5
     */
    public PropertyEditorSupport() {
        setSource(this);
    }

    /**
     * 構(gòu)造一個對象滑频,源不為自己捡偏,而是外部傳入。<br/>
     * 所謂源就是在事件觸發(fā)的時候可追溯的一個對象峡迷。
     * Constructs a <code>PropertyEditorSupport</code> object.
     *
     * @param source the source used for event firing
     * @since 1.5
     */
    public PropertyEditorSupport(Object source) {
        if (source == null) {
           throw new NullPointerException();
        }
        setSource(source);
    }

    /**
     * 獲取觸發(fā)事件對應(yīng)的源
     * Returns the bean that is used as the
     * source of events. If the source has not
     * been explicitly set then this instance of
     * <code>PropertyEditorSupport</code> is returned.
     *
     * @return the source object or this instance
     * @since 1.5
     */
    public Object getSource() {
        return source;
    }

    /**
     * 設(shè)置源
     * Sets the source bean.
     * <p>
     * The source bean is used as the source of events
     * for the property changes. This source should be used for information
     * purposes only and should not be modified by the PropertyEditor.
     *
     * @param source source object to be used for events
     * @since 1.5
     */
    public void setSource(Object source) {
        this.source = source;
    }

    /**
     * 設(shè)置屬性值银伟,同時觸發(fā)屬性變更的事件
     * Set (or change) the object that is to be edited.
     *
     * @param value The new target object to be edited.  Note that this
     *     object should not be modified by the PropertyEditor, rather
     *     the PropertyEditor should create a new object to hold any
     *     modified value.
     */
    public void setValue(Object value) {
        this.value = value;
        firePropertyChange();
    }

    /**
     * 獲取屬性值
     * Gets the value of the property.
     *
     * @return The value of the property.
     */
    public Object getValue() {
        return value;
    }

    //----------------------------------------------------------------------

    /**
     * 是否可繪制的,默認(rèn)為false
     * Determines whether the class will honor the paintValue method.
     *
     * @return  True if the class will honor the paintValue method.
     */

    public boolean isPaintable() {
        return false;
    }

    /**
     * 繪制屬性绘搞,默認(rèn)為空實現(xiàn)
     * Paint a representation of the value into a given area of screen
     * real estate.  Note that the propertyEditor is responsible for doing
     * its own clipping so that it fits into the given rectangle.
     * <p>
     * If the PropertyEditor doesn't honor paint requests (see isPaintable)
     * this method should be a silent noop.
     *
     * @param gfx  Graphics object to paint into.
     * @param box  Rectangle within graphics object into which we should paint.
     */
    public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
    }

    //----------------------------------------------------------------------

    /**
     * 獲取屬性初始的字符串彤避,默認(rèn)為三個英文問號
     * This method is intended for use when generating Java code to set
     * the value of the property.  It should return a fragment of Java code
     * that can be used to initialize a variable with the current property
     * value.
     * <p>
     * Example results are "2", "new Color(127,127,34)", "Color.orange", etc.
     *
     * @return A fragment of Java code representing an initializer for the
     *          current value.
     */
    public String getJavaInitializationString() {
        return "???";
    }

    //----------------------------------------------------------------------

    /**
     * 獲取屬性對應(yīng)的字符串,默認(rèn)調(diào)用屬性對象的toString方法夯辖,通常情況下需要子類自行重寫琉预。
     * Gets the property value as a string suitable for presentation
     * to a human to edit.
     *
     * @return The property value as a string suitable for presentation
     *       to a human to edit.
     * <p>   Returns null if the value can't be expressed as a string.
     * <p>   If a non-null value is returned, then the PropertyEditor should
     *       be prepared to parse that string back in setAsText().
     */
    public String getAsText() {
        return (this.value != null)
                ? this.value.toString()
                : null;
    }

    /**
     * 用字符串去更新屬性,如果屬性本身就是字符串蒿褂,直接設(shè)置即可圆米,否則拋出異常。
     * 子類可重寫此方法啄栓。
     * Sets the property value by parsing a given String.  May raise
     * java.lang.IllegalArgumentException if either the String is
     * badly formatted or if this kind of property can't be expressed
     * as text.
     *
     * @param text  The string to be parsed.
     */
    public void setAsText(String text) throws java.lang.IllegalArgumentException {
        if (value instanceof String) {
            setValue(text);
            return;
        }
        throw new java.lang.IllegalArgumentException(text);
    }

    //----------------------------------------------------------------------

    /**
     * 獲取標(biāo)簽娄帖,即:可羅列的字符串值。
     * If the property value must be one of a set of known tagged values,
     * then this method should return an array of the tag values.  This can
     * be used to represent (for example) enum values.  If a PropertyEditor
     * supports tags, then it should support the use of setAsText with
     * a tag value as a way of setting the value.
     *
     * @return The tag values for this property.  May be null if this
     *   property cannot be represented as a tagged value.
     *
     */
    public String[] getTags() {
        return null;
    }

    //----------------------------------------------------------------------

    /**
     * A PropertyEditor may chose to make available a full custom Component
     * that edits its property value.  It is the responsibility of the
     * PropertyEditor to hook itself up to its editor Component itself and
     * to report property value changes by firing a PropertyChange event.
     * <P>
     * The higher-level code that calls getCustomEditor may either embed
     * the Component in some larger property sheet, or it may put it in
     * its own individual dialog, or ...
     *
     * @return A java.awt.Component that will allow a human to directly
     *      edit the current property value.  May be null if this is
     *      not supported.
     */

    public java.awt.Component getCustomEditor() {
        return null;
    }

    /**
     * Determines whether the propertyEditor can provide a custom editor.
     *
     * @return  True if the propertyEditor can provide a custom editor.
     */
    public boolean supportsCustomEditor() {
        return false;
    }

    //----------------------------------------------------------------------

    /**
     * 設(shè)置監(jiān)聽事件
     * Adds a listener for the value change.
     * When the property editor changes its value
     * it should fire a {@link PropertyChangeEvent}
     * on all registered {@link PropertyChangeListener}s,
     * specifying the {@code null} value for the property name.
     * If the source property is set,
     * it should be used as the source of the event.
     * <p>
     * The same listener object may be added more than once,
     * and will be called as many times as it is added.
     * If {@code listener} is {@code null},
     * no exception is thrown and no action is taken.
     *
     * @param listener  the {@link PropertyChangeListener} to add
     */
    public synchronized void addPropertyChangeListener(
                                PropertyChangeListener listener) {
        if (listeners == null) {
            listeners = new java.util.Vector<>();
        }
        listeners.addElement(listener);
    }

    /**
     * 移除監(jiān)聽事件
     * Removes a listener for the value change.
     * <p>
     * If the same listener was added more than once,
     * it will be notified one less time after being removed.
     * If {@code listener} is {@code null}, or was never added,
     * no exception is thrown and no action is taken.
     *
     * @param listener  the {@link PropertyChangeListener} to remove
     */
    public synchronized void removePropertyChangeListener(
                                PropertyChangeListener listener) {
        if (listeners == null) {
            return;
        }
        listeners.removeElement(listener);
    }

    /**
     * 觸發(fā)監(jiān)聽事件
     * Report that we have been modified to any interested listeners.
     */
    public void firePropertyChange() {
        java.util.Vector<PropertyChangeListener> targets;
        synchronized (this) {
            if (listeners == null) {
                return;
            }
            targets = unsafeClone(listeners);
        }
        // Tell our listeners that "everything" has changed.
        PropertyChangeEvent evt = new PropertyChangeEvent(source, null, null, null);

        for (int i = 0; i < targets.size(); i++) {
            PropertyChangeListener target = targets.elementAt(i);
            target.propertyChange(evt);
        }
    }

    @SuppressWarnings("unchecked")
    private <T> java.util.Vector<T> unsafeClone(java.util.Vector<T> v) {
        return (java.util.Vector<T>)v.clone();
    }

    //----------------------------------------------------------------------

    private Object value;
    private Object source;
    private java.util.Vector<PropertyChangeListener> listeners;
}

PropertyEditorRegistry

PropertyEditorRegistry是一個PropertyEditor注冊器的接口昙楚,高度抽象了PropertyEditor的注冊流程块茁。

public interface PropertyEditorRegistry {

    /**
     * 注冊自定義編輯器
     * Register the given custom property editor for all properties of the given type.
     * @param requiredType the type of the property
     * @param propertyEditor the editor to register
     */
    void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);

    /**
     * 注冊自定義編輯器,同時指定屬性路徑
     */
    void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);

    /**
     * 根據(jù)類型和屬性路徑桂肌,查找屬性編輯器
     */
    @Nullable
    PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
}

PropertyEditorRegistrySupport

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {

    // 轉(zhuǎn)換服務(wù)
    @Nullable
    private ConversionService conversionService;

    // 默認(rèn)編輯器是否啟用
    private boolean defaultEditorsActive = false;
    
    // 配置屬性編輯器是否啟用
    private boolean configValueEditorsActive = false;

    // 默認(rèn)編輯器
    @Nullable
    private Map<Class<?>, PropertyEditor> defaultEditors;

    // 重寫的默認(rèn)編輯器
    @Nullable
    private Map<Class<?>, PropertyEditor> overriddenDefaultEditors;

    // 自定義屬性編輯器
    @Nullable
    private Map<Class<?>, PropertyEditor> customEditors;

    // 自定義路徑屬性編輯器
    @Nullable
    private Map<String, CustomEditorHolder> customEditorsForPath;

    // 自定義屬性編輯器緩存,僅僅當(dāng)緩存過之后永淌,才會存在這里面
    @Nullable
    private Map<Class<?>, PropertyEditor> customEditorCache;


    /**
     * 設(shè)置轉(zhuǎn)換服務(wù)
     * Specify a Spring 3.0 ConversionService to use for converting
     * property values, as an alternative to JavaBeans PropertyEditors.
     */
    public void setConversionService(@Nullable ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    /**
     * 返回轉(zhuǎn)換服務(wù)
     * Return the associated ConversionService, if any.
     */
    @Nullable
    public ConversionService getConversionService() {
        return this.conversionService;
    }


    //---------------------------------------------------------------------
    // Management of default editors
    //---------------------------------------------------------------------

    /**
     * Activate the default editors for this registry instance,
     * allowing for lazily registering default editors when needed.
     */
    protected void registerDefaultEditors() {
        this.defaultEditorsActive = true;
    }

    /**
     * Activate config value editors which are only intended for configuration purposes,
     * such as {@link org.springframework.beans.propertyeditors.StringArrayPropertyEditor}.
     * <p>Those editors are not registered by default simply because they are in
     * general inappropriate for data binding purposes. Of course, you may register
     * them individually in any case, through {@link #registerCustomEditor}.
     */
    public void useConfigValueEditors() {
        this.configValueEditorsActive = true;
    }

    /**
     * 重寫的默認(rèn)編輯器
     * Override the default editor for the specified type with the given property editor.
     * <p>Note that this is different from registering a custom editor in that the editor
     * semantically still is a default editor. A ConversionService will override such a
     * default editor, whereas custom editors usually override the ConversionService.
     * @param requiredType the type of the property
     * @param propertyEditor the editor to register
     * @see #registerCustomEditor(Class, PropertyEditor)
     */
    public void overrideDefaultEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
        if (this.overriddenDefaultEditors == null) {
            this.overriddenDefaultEditors = new HashMap<>();
        }
        this.overriddenDefaultEditors.put(requiredType, propertyEditor);
    }

    /**
     * 獲取特定類型的默認(rèn)編輯器
     * 1. 當(dāng)默認(rèn)編輯器不啟用時崎场,返回null
     * 2. 首先從重寫的默認(rèn)編輯器獲取
     * 3. 其次從默認(rèn)編輯器獲取
     * Retrieve the default editor for the given property type, if any.
     * <p>Lazily registers the default editors, if they are active.
     * @param requiredType type of the property
     * @return the default editor, or {@code null} if none found
     * @see #registerDefaultEditors
     */
    @Nullable
    public PropertyEditor getDefaultEditor(Class<?> requiredType) {
        if (!this.defaultEditorsActive) {
            return null;
        }
        if (this.overriddenDefaultEditors != null) {
            PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
            if (editor != null) {
                return editor;
            }
        }
        if (this.defaultEditors == null) {
            createDefaultEditors();
        }
        return this.defaultEditors.get(requiredType);
    }

    /**
     * 創(chuàng)建一系列的默認(rèn)編輯器
     * Actually register the default editors for this registry instance.
     */
    private void createDefaultEditors() {
        this.defaultEditors = new HashMap<>(64);

        // Simple editors, without parameterization capabilities.
        // The JDK does not contain a default editor for any of these target types.
        this.defaultEditors.put(Charset.class, new CharsetEditor());
        this.defaultEditors.put(Class.class, new ClassEditor());
        this.defaultEditors.put(Class[].class, new ClassArrayEditor());
        this.defaultEditors.put(Currency.class, new CurrencyEditor());
        this.defaultEditors.put(File.class, new FileEditor());
        this.defaultEditors.put(InputStream.class, new InputStreamEditor());
        this.defaultEditors.put(InputSource.class, new InputSourceEditor());
        this.defaultEditors.put(Locale.class, new LocaleEditor());
        this.defaultEditors.put(Path.class, new PathEditor());
        this.defaultEditors.put(Pattern.class, new PatternEditor());
        this.defaultEditors.put(Properties.class, new PropertiesEditor());
        this.defaultEditors.put(Reader.class, new ReaderEditor());
        this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
        this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
        this.defaultEditors.put(URI.class, new URIEditor());
        this.defaultEditors.put(URL.class, new URLEditor());
        this.defaultEditors.put(UUID.class, new UUIDEditor());
        this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());

        // Default instances of collection editors.
        // Can be overridden by registering custom instances of those as custom editors.
        this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
        this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
        this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
        this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
        this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

        // Default editors for primitive arrays.
        this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
        this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

        // The JDK does not contain a default editor for char!
        this.defaultEditors.put(char.class, new CharacterEditor(false));
        this.defaultEditors.put(Character.class, new CharacterEditor(true));

        // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
        this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
        this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

        // The JDK does not contain default editors for number wrapper types!
        // Override JDK primitive number editors with our own CustomNumberEditor.
        this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
        this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
        this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
        this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
        this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
        this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
        this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
        this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
        this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
        this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
        this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
        this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
        this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
        this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

        // Only register config value editors if explicitly requested.
        if (this.configValueEditorsActive) {
            StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
            this.defaultEditors.put(String[].class, sae);
            this.defaultEditors.put(short[].class, sae);
            this.defaultEditors.put(int[].class, sae);
            this.defaultEditors.put(long[].class, sae);
        }
    }

    /**
     * 拷貝默認(rèn)編輯器,包括參數(shù)等
     * Copy the default editors registered in this instance to the given target registry.
     * @param target the target registry to copy to
     */
    protected void copyDefaultEditorsTo(PropertyEditorRegistrySupport target) {
        target.defaultEditorsActive = this.defaultEditorsActive;
        target.configValueEditorsActive = this.configValueEditorsActive;
        target.defaultEditors = this.defaultEditors;
        target.overriddenDefaultEditors = this.overriddenDefaultEditors;
    }


    //---------------------------------------------------------------------
    // Management of custom editors
    //---------------------------------------------------------------------

    @Override
    public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
        registerCustomEditor(requiredType, null, propertyEditor);
    }

    // 注冊自定義編輯器
    // 1. 類型和屬性路徑不能全為null
    // 2. 屬性路徑不為null時遂蛀,默認(rèn)以屬性路徑的方式注冊
    // 3. 當(dāng)屬性路徑為null時谭跨,以類型的方式注冊,并清空自定義緩存編輯器
    @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);
            }
            this.customEditors.put(requiredType, propertyEditor);
            this.customEditorCache = null;
        }
    }

    /**
     * 查找自定義編輯器李滴,同樣的螃宙。
     * 1. 首先,以屬性路徑的方式進行查找
     * 2. 其次所坯,其類型的方式進行查找
     * 3. 特別需要注意的是`addStrippedPropertyPaths`方法谆扎,其目的是剝離掉其內(nèi)層的屬性。
     *    比如:當(dāng)nestedPath為`a[b[c[d]]]]`時芹助,我們調(diào)用此方法堂湖,返回了`a]]]`
     */
    @Override
    @Nullable
    public PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath) {
        Class<?> requiredTypeToUse = requiredType;
        if (propertyPath != null) {
            if (this.customEditorsForPath != null) {
                // Check property-specific editor first.
                PropertyEditor editor = getCustomEditor(propertyPath, requiredType);
                if (editor == null) {
                    List<String> strippedPaths = new ArrayList<>();
                    addStrippedPropertyPaths(strippedPaths, "", propertyPath);
                    for (Iterator<String> it = strippedPaths.iterator(); it.hasNext() && editor == null;) {
                        String strippedPath = it.next();
                        editor = getCustomEditor(strippedPath, requiredType);
                    }
                }
                if (editor != null) {
                    return editor;
                }
            }
            if (requiredType == null) {
                requiredTypeToUse = getPropertyType(propertyPath);
            }
        }
        // No property-specific editor -> check type-specific editor.
        return getCustomEditor(requiredTypeToUse);
    }

    /**
     * 確定是否存在特定類型或?qū)傩月窂降淖远x編輯器
     * Determine whether this registry contains a custom editor
     * for the specified array/collection element.
     * @param elementType the target type of the element
     * (can be {@code null} if not known)
     * @param propertyPath the property path (typically of the array/collection;
     * can be {@code null} if not known)
     * @return whether a matching custom editor has been found
     */
    public boolean hasCustomEditorForElement(@Nullable Class<?> elementType, @Nullable String propertyPath) {
        if (propertyPath != null && this.customEditorsForPath != null) {
            for (Map.Entry<String, CustomEditorHolder> entry : this.customEditorsForPath.entrySet()) {
                if (PropertyAccessorUtils.matchesProperty(entry.getKey(), propertyPath) &&
                        entry.getValue().getPropertyEditor(elementType) != null) {
                    return true;
                }
            }
        }
        // No property-specific editor -> check type-specific editor.
        return (elementType != null && this.customEditors != null && this.customEditors.containsKey(elementType));
    }

    /**
     * Determine the property type for the given property path.
     * <p>Called by {@link #findCustomEditor} if no required type has been specified,
     * to be able to find a type-specific editor even if just given a property path.
     * <p>The default implementation always returns {@code null}.
     * BeanWrapperImpl overrides this with the standard {@code getPropertyType}
     * method as defined by the BeanWrapper interface.
     * @param propertyPath the property path to determine the type for
     * @return the type of the property, or {@code null} if not determinable
     * @see BeanWrapper#getPropertyType(String)
     */
    @Nullable
    protected Class<?> getPropertyType(String propertyPath) {
        return null;
    }

    /**
     * 獲取自定義屬性編輯器
     * Get custom editor that has been registered for the given property.
     * @param propertyName the property path to look for
     * @param requiredType the type to look for
     * @return the custom editor, or {@code null} if none specific for this property
     */
    @Nullable
    private PropertyEditor getCustomEditor(String propertyName, @Nullable Class<?> requiredType) {
        CustomEditorHolder holder =
                (this.customEditorsForPath != null ? this.customEditorsForPath.get(propertyName) : null);
        return (holder != null ? holder.getPropertyEditor(requiredType) : null);
    }

    /**
     * 獲取自定義屬性編輯器闲先,這兒為了加快檢索速度,做了以下兩件事情
     * 1. 前置空校驗
     * 2. 后置緩存處理和查找
     * Get custom editor for the given type. If no direct match found,
     * try custom editor for superclass (which will in any case be able
     * to render a value as String via {@code getAsText}).
     * @param requiredType the type to look for
     * @return the custom editor, or {@code null} if none found for this type
     * @see java.beans.PropertyEditor#getAsText()
     */
    @Nullable
    private PropertyEditor getCustomEditor(@Nullable Class<?> requiredType) {
        if (requiredType == null || this.customEditors == null) {
            return null;
        }
        // Check directly registered editor for type.
        PropertyEditor editor = this.customEditors.get(requiredType);
        if (editor == null) {
            // Check cached editor for type, registered for superclass or interface.
            if (this.customEditorCache != null) {
                editor = this.customEditorCache.get(requiredType);
            }
            if (editor == null) {
                // Find editor for superclass or interface.
                for (Iterator<Class<?>> it = this.customEditors.keySet().iterator(); it.hasNext() && editor == null;) {
                    Class<?> key = it.next();
                    if (key.isAssignableFrom(requiredType)) {
                        editor = this.customEditors.get(key);
                        // Cache editor for search type, to avoid the overhead
                        // of repeated assignable-from checks.
                        if (this.customEditorCache == null) {
                            this.customEditorCache = new HashMap<>();
                        }
                        this.customEditorCache.put(requiredType, editor);
                    }
                }
            }
        }
        return editor;
    }

    /**
     * 推測屬性對應(yīng)的類型无蜂,這個方法會遞歸和遍歷所有匹配的路徑伺糠,效率相對較低
     * Guess the property type of the specified property from the registered
     * custom editors (provided that they were registered for a specific type).
     * @param propertyName the name of the property
     * @return the property type, or {@code null} if not determinable
     */
    @Nullable
    protected Class<?> guessPropertyTypeFromEditors(String propertyName) {
        if (this.customEditorsForPath != null) {
            CustomEditorHolder editorHolder = this.customEditorsForPath.get(propertyName);
            if (editorHolder == null) {
                List<String> strippedPaths = new ArrayList<>();
                addStrippedPropertyPaths(strippedPaths, "", propertyName);
                for (Iterator<String> it = strippedPaths.iterator(); it.hasNext() && editorHolder == null;) {
                    String strippedName = it.next();
                    editorHolder = this.customEditorsForPath.get(strippedName);
                }
            }
            if (editorHolder != null) {
                return editorHolder.getRegisteredType();
            }
        }
        return null;
    }

    /**
     * 拷貝當(dāng)前對象,做的是淺拷貝的方式
     * Copy the custom editors registered in this instance to the given target registry.
     * @param target the target registry to copy to
     * @param nestedProperty the nested property path of the target registry, if any.
     * If this is non-null, only editors registered for a path below this nested property
     * will be copied. If this is null, all editors will be copied.
     */
    protected void copyCustomEditorsTo(PropertyEditorRegistry target, @Nullable String nestedProperty) {
        String actualPropertyName =
                (nestedProperty != null ? PropertyAccessorUtils.getPropertyName(nestedProperty) : null);
        if (this.customEditors != null) {
            this.customEditors.forEach(target::registerCustomEditor);
        }
        if (this.customEditorsForPath != null) {
            this.customEditorsForPath.forEach((editorPath, editorHolder) -> {
                if (nestedProperty != null) {
                    int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(editorPath);
                    if (pos != -1) {
                        String editorNestedProperty = editorPath.substring(0, pos);
                        String editorNestedPath = editorPath.substring(pos + 1);
                        if (editorNestedProperty.equals(nestedProperty) || editorNestedProperty.equals(actualPropertyName)) {
                            target.registerCustomEditor(
                                    editorHolder.getRegisteredType(), editorNestedPath, editorHolder.getPropertyEditor());
                        }
                    }
                }
                else {
                    target.registerCustomEditor(
                            editorHolder.getRegisteredType(), editorPath, editorHolder.getPropertyEditor());
                }
            });
        }
    }


    /**
     * Add property paths with all variations of stripped keys and/or indexes.
     * Invokes itself recursively with nested paths.
     * @param strippedPaths the result list to add to
     * @param nestedPath the current nested path
     * @param propertyPath the property path to check for keys/indexes to strip
     */
    private void addStrippedPropertyPaths(List<String> strippedPaths, String nestedPath, String propertyPath) {
        int startIndex = propertyPath.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR);
        if (startIndex != -1) {
            int endIndex = propertyPath.indexOf(PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR);
            if (endIndex != -1) {
                String prefix = propertyPath.substring(0, startIndex);
                String key = propertyPath.substring(startIndex, endIndex + 1);
                String suffix = propertyPath.substring(endIndex + 1, propertyPath.length());
                // Strip the first key.
                strippedPaths.add(nestedPath + prefix + suffix);
                // Search for further keys to strip, with the first key stripped.
                addStrippedPropertyPaths(strippedPaths, nestedPath + prefix, suffix);
                // Search for further keys to strip, with the first key not stripped.
                addStrippedPropertyPaths(strippedPaths, nestedPath + prefix + key, suffix);
            }
        }
    }

    /**
     * 自定義屬性持有器斥季,支持以類型繼承的方式進行查找
     * Holder for a registered custom editor with property name.
     * Keeps the PropertyEditor itself plus the type it was registered for.
     */
    private static final class CustomEditorHolder {

        private final PropertyEditor propertyEditor;

        @Nullable
        private final Class<?> registeredType;

        private CustomEditorHolder(PropertyEditor propertyEditor, @Nullable Class<?> registeredType) {
            this.propertyEditor = propertyEditor;
            this.registeredType = registeredType;
        }

        private PropertyEditor getPropertyEditor() {
            return this.propertyEditor;
        }

        @Nullable
        private Class<?> getRegisteredType() {
            return this.registeredType;
        }

        @Nullable
        private PropertyEditor getPropertyEditor(@Nullable Class<?> requiredType) {
            // Special case: If no required type specified, which usually only happens for
            // Collection elements, or required type is not assignable to registered type,
            // which usually only happens for generic properties of type Object -
            // then return PropertyEditor if not registered for Collection or array type.
            // (If not registered for Collection or array, it is assumed to be intended
            // for elements.)
            if (this.registeredType == null ||
                    (requiredType != null &&
                    (ClassUtils.isAssignable(this.registeredType, requiredType) ||
                    ClassUtils.isAssignable(requiredType, this.registeredType))) ||
                    (requiredType == null &&
                    (!Collection.class.isAssignableFrom(this.registeredType) && !this.registeredType.isArray()))) {
                return this.propertyEditor;
            }
            else {
                return null;
            }
        }
    }

}

PropertyAccessorUtils

其內(nèi)部是一系列的工具方法训桶,在配合bean屬性解析時可考慮對比查看。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酣倾,一起剝皮案震驚了整個濱河市舵揭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灶挟,老刑警劉巖琉朽,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異稚铣,居然都是意外死亡箱叁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門惕医,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耕漱,“玉大人,你說我怎么就攤上這事抬伺∶唬” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵峡钓,是天一觀的道長妓笙。 經(jīng)常有香客問我,道長能岩,這世上最難降的妖魔是什么寞宫? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮拉鹃,結(jié)果婚禮上辈赋,老公的妹妹穿的比我還像新娘。我一直安慰自己膏燕,他們只是感情好钥屈,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坝辫,像睡著了一般篷就。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阀溶,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天腻脏,我揣著相機與錄音鸦泳,去河邊找鬼。 笑死永品,一個胖子當(dāng)著我的面吹牛做鹰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鼎姐,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼钾麸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炕桨?” 一聲冷哼從身側(cè)響起饭尝,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎献宫,沒想到半個月后钥平,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡姊途,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年涉瘾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捷兰。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡立叛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贡茅,到底是詐尸還是另有隱情秘蛇,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布顶考,位于F島的核電站赁还,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驹沿。R本人自食惡果不足惜秽浇,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甚负。 院中可真熱鬧,春花似錦审残、人聲如沸梭域。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽病涨。三九已至,卻和暖如春璧坟,著一層夾襖步出監(jiān)牢的瞬間既穆,已是汗流浹背赎懦。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留幻工,地道東北人励两。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像囊颅,于是被迫代替她去往敵國和親当悔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344