Spring 中的各种 Editor,其实都是java.beans包中PropertyEditor的实现类

java Bean规范中有这样一个接口,PropertyEditor,从这个接口的名字来看,是用来进行编辑属性的,那自然是编辑对象的属性。

        1.为什么需要属性编辑器呢

        我们通常会在类型定义各种类型的属性,通常我们自己通过new创建对象,并且设置属性的时候,我们知道每个字段是什么类型,我们就会设置一个对应类型的值给指定的字段。但是有的时候我们需要将一个字符串,转换为某个字段的类型,因为有些类型,如果是从字符串转换为该类型,比较复杂,所以我们就需要一个工具类来抽取转换的代码,将字符串转换为指定的类型。所以java.beans中定义了PropertyEditor,该接口中定义的void setAsText(String text) 就是将传入的字符串text转换为指定类型的对象,为了方便使用,java为这个接口定义了一个默认的实现类PropertyEditorSupport类,它帮助我们实现了所有的方法,我们只需要继承它就可以使用了。在PropertyEditorSupport定义了一个value的字段,setAsText()中 将字符串转化为指定类型的对象以后,通过setValue() 把该对象赋值给 value字段,这样我们就可以通过getValue()方法获取到我们想要的对象

        2.属性编辑器能完成什么样的功能

        (1) 一个PropertyEditor的实现类,可以帮我们完成把一个字符串转换为一个我们想要的对象

        (2)该类还定义了一个 String getAsText() ,主要就是返回一个能代表指定类型的对象,并且我们看得懂的字符串

        3.在Spring中应用场景

        在Spring中我们有大量的这种场景,比如我们在一个类中定义了一个URL类型的字段,但是我们在xml文件中通过Property标签配置该字段是只能配置字符串类型,所以就需要一个PropertyEditor 将我们配置的 字符串转换为 URL类型的对象。比如在SpringMvc中,我们定义的接收参数的Model中可能有各种各样类型的字段,有时候还可能定义一个数组类型的字段,但是前端传过来的参数都是字符串,所以就需要有对应的PropertyEditor将字符串转换为想要的类型的对象

我们自定义一个PropertyEditor来测试一下

public class PersonEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        String[] split = text.split("#");
        Person person = new Person();
        person.setName(split[0]);
        person.setAge(Integer.parseInt(split[1]));
        this.setValue(person);
    }

    @Override
    public String getAsText() {
        return ((Person)this.getValue()).getName() + "#" + ((Person)this.getValue()).getAge();
    }

    public static void main(String[] args) {
        PersonEditor personEditor = new PersonEditor();
        personEditor.setAsText("孙悟空#30");
        System.out.println(personEditor.getValue());
        System.out.println(personEditor.getAsText());
    }
}

运行结果:
Person{name='孙悟空', age=30}
孙悟空#30

可以看到我们成功将字符串  孙悟空#30  转换为一个Person对象,这样如果我们在接收参数的Model中定义了Person类型的字段,前端传了 孙悟空#30 ,我们就可以用PersonEditor来将

孙悟空#30  转换为一个Person对象

        4.接下来介绍一个Spring自己定义好的PropertyEditor,ResourceEditor

Editor for Resource descriptors, to automatically convert String locations e.g. file:C:/myfile.txt or classpath:myfile.txt to Resource properties instead of using a String location property.
The path may contain ${...} placeholders, to be resolved as org.springframework.core.env.Environment properties: e.g. ${user.dir}. Unresolvable placeholders are ignored by default.

资源描述符的编辑器,自动将字符串,例如:file:C:/myfile.txt 或者 classpath:myfile.txt 转换为Resource类型的属性,而不是使用字符串类型的属性。
以上这句话的意思就是,如果一个类的属性是 Resource 类型的,但是我们知道资源的字符串路径,这时候就需要通过ResourceEditor将字符串路径转换为Resource类型的对象,赋值给Resource 类型的属性。

字符串路径可能包含 ${...} 占位符,这个占位符将解析为环境中的变量,例如:${user.dir}就会被环境中的 user.dir 替换掉,解析不了的占位符默认会被忽略

public class ResourceEditor extends PropertyEditorSupport {

	private final ResourceLoader resourceLoader;

	@Nullable
	private PropertyResolver propertyResolver;

	private final boolean ignoreUnresolvablePlaceholders;


	/**
	 * Create a new instance of the {@link ResourceEditor} class
	 * using a {@link DefaultResourceLoader} and {@link StandardEnvironment}.
	 */
	public ResourceEditor() {
		this(new DefaultResourceLoader(), null);
	}

	/**
	 * Create a new instance of the {@link ResourceEditor} class
	 * using the given {@link ResourceLoader} and {@link PropertyResolver}.
	 * @param resourceLoader the {@code ResourceLoader} to use
	 * @param propertyResolver the {@code PropertyResolver} to use
	 */
	public ResourceEditor(ResourceLoader resourceLoader, @Nullable PropertyResolver propertyResolver) {
		this(resourceLoader, propertyResolver, true);
	}

	/**
	 * Create a new instance of the {@link ResourceEditor} class
	 * using the given {@link ResourceLoader}.
	 * @param resourceLoader the {@code ResourceLoader} to use
	 * @param propertyResolver the {@code PropertyResolver} to use
	 * @param ignoreUnresolvablePlaceholders whether to ignore unresolvable placeholders
	 * if no corresponding property could be found in the given {@code propertyResolver}
	 */
	public ResourceEditor(ResourceLoader resourceLoader, @Nullable PropertyResolver propertyResolver,
			boolean ignoreUnresolvablePlaceholders) {
        
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
        // 根据上面的说明 要把字符串转化为一个Resource对象,首先需要一个资源加载器按照字符串路        
        // 径加载资源
		this.resourceLoader = resourceLoader;
        // 字符串路径中包含占位符,所以需要PropertyResolver来解析占位符
		this.propertyResolver = propertyResolver;
		this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
	}


	@Override
	public void setAsText(String text) {
		if (StringUtils.hasText(text)) {
            // 这里的转换过程很简单,就是先解析路径中的占位符,得到完整的路径
            // 然后通过资源加载器将路径加载为一个Resource对象,通过setValue()
            // 方法将Resource对象赋值给 value 字段,这样其他组件就可以通过
            // getValue() 获取到该Resource对象
			String locationToUse = resolvePath(text).trim();
			setValue(this.resourceLoader.getResource(locationToUse));
		}
		else {
			setValue(null);
		}
	}

	/**
	 * Resolve the given path, replacing placeholders with corresponding
	 * property values from the {@code environment} if necessary.
	 * @param path the original file path
	 * @return the resolved file path
	 * @see PropertyResolver#resolvePlaceholders
	 * @see PropertyResolver#resolveRequiredPlaceholders
	 */
	protected String resolvePath(String path) {
		if (this.propertyResolver == null) {
            // 如果构造方法中没有指定propertyResolver,默认使用
            // StandardEnvironment,StandardEnvironment实现了
            // PropertyResolver
			this.propertyResolver = new StandardEnvironment();
		}
		return (this.ignoreUnresolvablePlaceholders ? this.propertyResolver.resolvePlaceholders(path) :
				this.propertyResolver.resolveRequiredPlaceholders(path));
	}


	@Override
	@Nullable
	public String getAsText() {
		Resource value = (Resource) getValue();
		try {
			// Try to determine URL for resource.
			return (value != null ? value.getURL().toExternalForm() : "");
		}
		catch (IOException ex) {
			// Couldn't determine resource URL - return null to indicate
			// that there is no appropriate text representation.
			return null;
		}
	}

}

        5.Spring 中常用的PropertyEditor

/**
 * PropertyEditorRegistrar implementation that populates a given
 * {@link org.springframework.beans.PropertyEditorRegistry}
 * (typically a {@link org.springframework.beans.BeanWrapper} used for bean
 * creation within an {@link org.springframework.context.ApplicationContext})
 * with resource editors. Used by
 * {@link org.springframework.context.support.AbstractApplicationContext}.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 2.0
 */
public class ResourceEditorRegistrar implements PropertyEditorRegistrar {

	private final PropertyResolver propertyResolver;

	private final ResourceLoader resourceLoader;


	/**
	 * Create a new ResourceEditorRegistrar for the given {@link ResourceLoader}
	 * and {@link PropertyResolver}.
	 * @param resourceLoader the ResourceLoader (or ResourcePatternResolver)
	 * to create editors for (usually an ApplicationContext)
	 * @param propertyResolver the PropertyResolver (usually an Environment)
	 * @see org.springframework.core.env.Environment
	 * @see org.springframework.core.io.support.ResourcePatternResolver
	 * @see org.springframework.context.ApplicationContext
	 */
	public ResourceEditorRegistrar(ResourceLoader resourceLoader, PropertyResolver propertyResolver) {
		this.resourceLoader = resourceLoader;
		this.propertyResolver = propertyResolver;
	}


	/**
	 * Populate the given {@code registry} with the following resource editors:
	 * ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
	 * URIEditor, ClassEditor, ClassArrayEditor.
	 * <p>If this registrar has been configured with a {@link ResourcePatternResolver},
	 * a ResourceArrayPropertyEditor will be registered as well.
	 * @see org.springframework.core.io.ResourceEditor
	 * @see org.springframework.beans.propertyeditors.InputStreamEditor
	 * @see org.springframework.beans.propertyeditors.InputSourceEditor
	 * @see org.springframework.beans.propertyeditors.FileEditor
	 * @see org.springframework.beans.propertyeditors.URLEditor
	 * @see org.springframework.beans.propertyeditors.URIEditor
	 * @see org.springframework.beans.propertyeditors.ClassEditor
	 * @see org.springframework.beans.propertyeditors.ClassArrayEditor
	 * @see org.springframework.core.io.support.ResourceArrayPropertyEditor
	 */
	@Override
	public void registerCustomEditors(PropertyEditorRegistry registry) {
        // 容器启动时,会给beanFactory的添加一个ResourceEditorRegistrar对象
        // beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, 
        // getEnvironment()));
        // 当调用这个ResourceEditorRegistrar对象的registerCustomEditors方法时,
        // 会将下面所有的PropertyEditor注册到传过来的PropertyEditorRegistry对象
		ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
		doRegisterEditor(registry, Resource.class, baseEditor);
		doRegisterEditor(registry, ContextResource.class, baseEditor);
		doRegisterEditor(registry, WritableResource.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));
		}
	}

	/**
	 * Override default editor, if possible (since that's what we really mean to do here);
	 * otherwise register as a custom editor.
	 */
	private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
		if (registry instanceof PropertyEditorRegistrySupport) {
			((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
		}
		else {
			registry.registerCustomEditor(requiredType, editor);
		}
	}

}

org.springframework.beans.factory.support.AbstractBeanFactory#getTypeConverter()

@Override
public TypeConverter getTypeConverter() {
	TypeConverter customConverter = getCustomTypeConverter();
	if (customConverter != null) {
		return customConverter;
	}
	else {
		// Build default TypeConverter, registering custom editors.
		SimpleTypeConverter typeConverter = new SimpleTypeConverter();
		typeConverter.setConversionService(getConversionService());
        //SimpleTypeConverter 其实也实现了 PropertyEditorRegistry
		registerCustomEditors(typeConverter);
		return typeConverter;
	}
}

org.springframework.beans.factory.support.AbstractBeanFactory#registerCustomEditors(PropertyEditorRegistry registry)

/**
 * Initialize the given PropertyEditorRegistry with the custom editors
 * that have been registered with this BeanFactory.
 * <p>To be called for BeanWrappers that will create and populate bean
 * instances, and for SimpleTypeConverter used for constructor argument
 * and factory method type conversion.
 * @param registry the PropertyEditorRegistry to initialize
 */
protected void registerCustomEditors(PropertyEditorRegistry registry) {
	if (registry instanceof PropertyEditorRegistrySupport) {
		((PropertyEditorRegistrySupport) registry).useConfigValueEditors();
	}
	// 在启动容器的时候,org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
	// 会给beanFactory中添加 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
	// 所以默认propertyEditorRegistrars 中会有一个ResourceEditorRegistrar对象
	// 当org.springframework.beans.factory.support.AbstractBeanFactory#getTypeConverter() 中调用本方法时,就会把
	// ResourceEditorRegistrar.registerCustomEditors(PropertyEditorRegistry registry) 方法中写死的那些 PropertyEditor
	// 全都注册到新创建的 SimpleTypeConverter typeConverter = new SimpleTypeConverter();
	// SimpleTypeConverter 其实也实现了 PropertyEditorRegistry
	if (!this.propertyEditorRegistrars.isEmpty()) {
		for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
			try {
				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)));
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值