Spring 源码系列
Spring 整体架构
编译Spring5.2.0源码
Spring-AliasRegistry 别名注册
Spring 资源加载
Spring 容器初始化
Spring 获取单例(一)
Spring 获取单例(二)
Spring 获取单例(三)
Spring 解决循环依赖
Spring FactoryBean 缓存
Spring Aware 介绍
Spring BeanPostProcessor 介绍
扯淡 Spring BeanDefinition
[探秘 Spring 的 PropertyEditor]()

PropertyEditor & PropertyEditorSupport 介绍

java.beans.PropertyEditor 是 JDK 自带的类,是提供给 AWT。做啥用呢、就是讲用户在图形见面中输出的字符串转换位对应类型的值(对象)。相似于一个 convertor。

public interface PropertyEditor {    void setValue(Object value);    Object getValue();    boolean isPaintable();    String getJavaInitializationString();    String getAsText();    void setAsText(String text) throws java.lang.IllegalArgumentException;    String[] getTags();    java.awt.Component getCustomEditor();    boolean supportsCustomEditor();    void addPropertyChangeListener(PropertyChangeListener listener);    void removePropertyChangeListener(PropertyChangeListener listener);}

次要办法有四个

  • void setValue(Object value); 设置属性值
  • Object getValue(); 获取属性值
  • String getAsText(); 把属性值转换成 String
  • void setAsText(String text); 把 String 转换成属性值

而 Java 也为咱们提供了一个默认的实现类 java.beans.PropertyEditorSupport

private Object value;public void setValue(Object value) {    this.value = value;    firePropertyChange();}public Object getValue() {    return value;}public void setAsText(String text) throws java.lang.IllegalArgumentException {    if (value instanceof String) {        setValue(text);        return;    }    throw new java.lang.IllegalArgumentException(text);}public String getAsText() {    return (this.value != null)    ? this.value.toString()    : null;}

咱们只有重写 setAsTextgetAsText 办法能够实现 String 类型到特定类型的转换了

与 Spring 的关系

说了那么久、这个跟 Spring 有什么锤子关系吗 ?

咱们想一想、当你应用 xml 配置文件给某个属性设定某个值的时候(或者说应用 @Value 注解给定一个默认值的时候)、咱们输出的是不是一个字符串、然而咱们对应的这个属性的类型却不肯定是字符串类型、这种场景之下、是不是跟 AWT 的场景是一样的。所以 Spring 的属性解释都是继承自 PropertyEditorSupport 而后重写了 setAsTextgetAsText

举个例子

public class CustomBooleanEditor extends PropertyEditorSupport {   public static final String VALUE_TRUE = "true";   public static final String VALUE_FALSE = "false";   public static final String VALUE_ON = "on";   public static final String VALUE_OFF = "off";   public static final String VALUE_YES = "yes";   public static final String VALUE_NO = "no";   public static final String VALUE_1 = "1";   public static final String VALUE_0 = "0";    // 为 true 的时候的字符串、默认为 null   @Nullable   private final String trueString;        // 为 false 的时候的字符串、默认为 null   @Nullable   private final String falseString;    // 是否容许为 null     // 根本类型 boolean 的时候不容许空的字符串    // 援用类型 Boolean 的时候容许空的字符串   private final boolean allowEmpty;    public CustomBooleanEditor(boolean allowEmpty) {      this(null, null, allowEmpty);   }    public CustomBooleanEditor(@Nullable String trueString, @Nullable String falseString, boolean allowEmpty) {      this.trueString = trueString;      this.falseString = falseString;      this.allowEmpty = allowEmpty;   }   @Override   public void setAsText(@Nullable String text) throws IllegalArgumentException {              String input = (text != null ? text.trim() : null);      if (this.allowEmpty && !StringUtils.hasLength(input)) {         // Treat empty String as null value.         setValue(null);      } else if (this.trueString != null && this.trueString.equalsIgnoreCase(input)) {         setValue(Boolean.TRUE);      } else if (this.falseString != null && this.falseString.equalsIgnoreCase(input)) {         setValue(Boolean.FALSE);      } else if (this.trueString == null &&            (VALUE_TRUE.equalsIgnoreCase(input) || VALUE_ON.equalsIgnoreCase(input) ||                  VALUE_YES.equalsIgnoreCase(input) || VALUE_1.equals(input))) {         setValue(Boolean.TRUE);      } else if (this.falseString == null &&            (VALUE_FALSE.equalsIgnoreCase(input) || VALUE_OFF.equalsIgnoreCase(input) ||                  VALUE_NO.equalsIgnoreCase(input) || VALUE_0.equals(input))) {         setValue(Boolean.FALSE);      } else {         throw new IllegalArgumentException("Invalid boolean value [" + text + "]");      }   }   @Override   public String getAsText() {      if (Boolean.TRUE.equals(getValue())) {         return (this.trueString != null ? this.trueString : VALUE_TRUE);      } else if (Boolean.FALSE.equals(getValue())) {         return (this.falseString != null ? this.falseString : VALUE_FALSE);      } else {         return "";      }   }}

办法也是挺简略的就不啰嗦解释了

举个例子

public class Job {   private boolean completed;   private Boolean started;   // get and set ...........}
<bean class="com.demo.property.editor.Job" id="job">   <property name="completed" value="on" />   <property name="started" value=""/></bean>

获取这个 bean 并打印 Job{completed=true, started=null}

相干组件介绍

PropertyEditorRegistry

一看名字就晓得是一个注册的接口

void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);@NullablePropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);

PropertyEditorRegistrySupport

PropertyEditorRegistry 的实现类。当咱们尝试去通过 Class 对象获取对应的 PropertyEditor 的时候、它会为咱们初始化一系列默认的 PropertyEditor

在 doCreateBean 的 populateBean 中会调用 getDefaultEditor 获取对应的 PropertyEditor 进行值的类型转换

// spring 默认提供的 propertyEditor@Nullableprivate Map<Class<?>, PropertyEditor> defaultEditors;// 去笼罩的 默认的 property editor@Nullableprivate Map<Class<?>, PropertyEditor> overriddenDefaultEditors;// 自定义的一些 property editor@Nullableprivate Map<Class<?>, PropertyEditor> customEditors;// 属性的门路/属性名,CustomEditorHolder 蕴含的是 Class 和 PropertyEditor@Nullableprivate Map<String, CustomEditorHolder> customEditorsForPath;// 如果注册的父 class、那么子类的 class 找不到的时候、就会返回这个父的 class 并且讲这个关系保留在// 这个 map 中@Nullableprivate Map<Class<?>, PropertyEditor> customEditorCache;@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);    }    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);        }    }

BeanWrapper

Spring 中用于封装 bean 的是 BeanWrapper 类型、而它又间接继承了 PropertyEditorRegistry。BeanWrapperImpl 是 BeanWrapper 的实现类、咱们在零碎中看到的大多数 PropertyEditorRegistry 都是 BeanWrapperImpl 的对象。BeanWrapperImpl 还继承了 PropertyEditorRegistrySupport 这个实现类

PropertyEditorRegistrar

property editor 的登记处

void registerCustomEditors(PropertyEditorRegistry registry);

ResourceEditorRegistrar

惟一的一个默认的实现类

public class ResourceEditorRegistrar implements PropertyEditorRegistrar {   private final PropertyResolver propertyResolver;   private final ResourceLoader resourceLoader;   public ResourceEditorRegistrar(ResourceLoader resourceLoader, PropertyResolver propertyResolver) {      this.resourceLoader = resourceLoader;      this.propertyResolver = propertyResolver;   }   @Override   public void registerCustomEditors(PropertyEditorRegistry registry) {      ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);      doRegisterEditor(registry, Resource.class, baseEditor);      doRegisterEditor(registry, ContextResource.class, baseEditor);      doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));      doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));      doRegisterEditor(registry, File.class, new FileEditor(baseEditor));      doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));      doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));      doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));      ClassLoader classLoader = this.resourceLoader.getClassLoader();      doRegisterEditor(registry, URI.class, new URIEditor(classLoader));      doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));      doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));      if (this.resourceLoader instanceof ResourcePatternResolver) {         doRegisterEditor(registry, Resource[].class,               new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));      }   }   private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {      if (registry instanceof PropertyEditorRegistrySupport) {         ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);      }      else {         registry.registerCustomEditor(requiredType, editor);      }   }}

先说下这个类被应用到的中央吧、只有应用 ApplicationContext 的时候这个 Registrar 才会被应用到、下面的 PropertyEditor 才会去注册或者笼罩 PropertyEditorRegistry 默认的值

调用关系链为

ClassPathXmlApplicationContext 构造函数 -> refresh -> prepareBeanFactory() -> 创立 ResourceEditorRegistrar 减少到 Set 中

继而它会在 doCreateBean 的 createBeanInstance 中将 ResourceEditorRegistrar 的默认的 PropertyEditor 注册进去

例子

public class Job {    private boolean completed;    private Content content;    // get and set method}
public class Content {    private String details;    private String type;    private int priority;    // get and set method}
<bean class="com.demo.property.editor.Job" id="job" lazy-init="true">   <property name="completed" value="off" />   <property name="content" value="关注我:紧急:100"/></bean>
ClassPathXmlApplicationContext classPathXmlApplicationContext =      new ClassPathXmlApplicationContext("property.editor/coderLi.xml");classPathXmlApplicationContext.getBeanFactory().addPropertyEditorRegistrar(registry -> {   if (registry instanceof PropertyEditorRegistrySupport) {      ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(Content.class, new ContentPropertyEditor());      System.out.println("PropertyEditorRegistrySupport");   } else {      registry.registerCustomEditor(Content.class, new ContentPropertyEditor());   }});Object job = classPathXmlApplicationContext.getBean("job");System.out.println(job);

实现雷同成果的办法有很多、比如说 CustomEditorConfigurer、也能够实现 BeanFactoryPostProcessor 接口等等

如我下面的代码实现的话、留神一个点就是、这个 bean 必须是一个提早实例化的、因为 ApplicationContext 默认是会将所有的非 lazy 的 bean 实例化、而这个时候咱们的 PropertyEditor 还没有注册进去、将会报错