一、前言
在这一篇博客中我们主要是介绍prepareBeanFactory方法,bean工厂的初始化准备工作,设置bean工厂的一些属性值,需要忽略哪一些接口,需要注册哪一些bean,需要设置哪一些bean的后置处理器等。接下来我们看具体的源码分析,并且这里我们还看看Spring属性编辑器,以及我们如果去拓展一个属性编辑器。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
// 设置SPEL表达式解析器,用来支持Spring的SPEL表达式
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 添加属性编辑注册器。例如一个字符串类型的地址需要转换为一个Address对象,可以使用该功能.
// 可参考示例:spring-source-study模块下的com.wb.spring.propertyeditor包下的示例程序
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 添加bean的后置处理器。此处添加的是Spring自己的后置处理器,用来回调bean所实现的aware接口中的方法.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 下面的ignoreDependencyInterface是用来设置bean工厂中需要忽略的接口
// 可以通过实现EnvironmentAware接口来获取到当前的环境信息Environment。
/**
* 如果将EnvironmentAware接口添加到ignoreDependencyInterface中,则在使用的地方通过@Autowired将会无法正常注入
* 而是需要通过setEnvironment方法进行注入,下面的其他接口都类似.
*/
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// 可以通过实现EmbeddedValueResolverAware接口来获取String类型值的解析器
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
// 资源加载器,例如使用:@Autowired ResourceLoaderAware aware; 将不会被注入
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
// 事件发布器
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
// 消息资源
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
// 应用的上下文信息
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// 注册一些可以自动装配的接口。 当类型为dependencyType时, 注入autowiredValue。为了解决一个类型有多个子类实现时,优先注入那个子类实现的问题。
// 例如下面第一个,当注入类型为BeanFactory时,注入的值为beanFactory,默认为DefaultListableBeanFactory
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
// 当注入类型为ResourceLoader时,注入的值为ApplicationContext
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
// 当注入类型为ApplicationEventPublisher时,注入的值为ApplicationContext
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
// 当注入的类型为ApplicationContext时,注入的值为ApplicationContext.
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 增加一个bean的后置处理器,ApplicationListenerDetector
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 如果bean工厂中存在着名称为loadTimeWeaver的bean定义,则给bean工厂中加入LoaderTimeWeaverAwareProcessor后置处理器
// 补充:1、增加对AspectJ的支持,在Java中织入分为3种:(1) 编译期织入; (2) 类加载期织入; (3) 运行期织入。编译期织入指的是在java编译期,
// 采用特殊的编译器,将切面织入到java类中;类加载期织入则指的是通过特殊的类加载器,在字节码加载到JVM时,织入切面;运行期织入则是
// 通过采用cglib或者jdk进行切面的织入。
// 2、aspectJ中提供了两种方式:
// (1) 通过特殊编译器,在编译期,将aspectJ语言编写的切面类织入到java类中;
// (2) 类加载期织入,就是通过下面的LoadTimeWeaving
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// 给容器中注册一些与运行环境相关的单实例Bean
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
// beanName: environment,直接将new出来的Spring内部对象放入到Spring的单实例缓存池中.
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
// beanName: systemProperties 方法:System.getProperties();
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
// beanName: systemEnvironment, 方法:System.getEnv();
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
我们可以看得到他是实现PropertyEditorRegistrar接口,里面只有一个方法registerCustomEditors就是用来注册我们的属性编辑器。这里注册的是一些Spring默认的属性编辑器。
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.
* 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) {
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));
}
}
/**
* 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);
}
}
}
现在我们如果需要实现自定义的一个属性编辑器,我们改如何去做呢?就好比我们现在要将字符串解析成我们对应的地址,在实际的项目需求中,比如说是string转成成我们的localDate等需求。
到这里我们首先要看明确一个东西,这些属性编辑器的时候是在什么时候进行调用,我们看下createBeanInstance方法的源码。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 解析Bean的Class类型
Class> beanClass = resolveBeanClass(mbd, beanName);
// 如果Bean不是public,而且是不允许共有权限访问,直接抛出异常.
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
/**
* 通过指定的回调方法去创建bean实例,Spring5.0版本之后新增的方法。
* 可以通过实现BeanFactoryPostProcessor接口来进行扩展,设置自定义的Supplier,通过自定义supplier实例化对象
*/
// 可参考:【示例代码:com.wb.spring.supplier.SupplierBeanFactoryPostProcessor】
Supplier> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
/**
* 如果当前bean指定了对应的工厂方法,则通过工厂方法去创建bean实例
* 底层会获取工厂方法【静态工厂方法|实例化方法】--> 然后解析方法入参 --> 然后执行反射调用创建实例 --> 封装为包装对象返回.
*
* fixme: 该方法的实现特别长 230行左右的代码
*/
if (mbd.getFactoryMethodName() != null) {
// 通过工厂方法创建实例,可参考:【示例代码:com.wb.spring.factorymethod.InstanceFactoryMethod,com.wb.spring.factorymethod.StaticFactoryMethod】
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
// 原型(prototype),多次获取的时候会走该分支
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
// 因为一个类可能由多个构造函数,所以需要根据配置文件中配置的参数或传入的参数来确定最终调用的构造函数。
// 因为判断过程会比较,所以spring会将解析、确定好的构造函数缓存到BeanDefinition中的resolvedConstructorOrFactoryMethod字段中。
// 在下次创建相同时直接从RootBeanDefinition中的属性resolvedConstructorOrFactoryMethod缓存的值获取,避免再次解析
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
// 表示构造函数的参数是否已经解析妥当
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
// 如果构造函数的参数已经解析妥当
if (autowireNecessary) {
// 则通过构造函数完成实例的创建
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 使用默认的bean对象创建策略进行bean对象的创建 ***【此处使用了策略设计模式】***
return instantiateBean(beanName, mbd);
}
}
/** 推断构造方法,获取候选的用来创建bean对象的构造函数 */
Constructor>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 获取到了构造函数 || 注入模式为使用构造函数 || bean定义中指定了带参数的构造函数 || 创建bean对象时的入参args[参数列表对应的值列表]不为null
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
// 使用构造函数及入参进行bean对象的创建
return autowireConstructor(beanName, mbd, ctors, args);
}
/** 获取指定的用来创建bean对象的默认构造函数 */
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
/**
* 如果上述情况都没有:没有创建bean的回调方法 && 没有工厂方法 && 构造函数的参数未解析完毕 && 没有预先指定的默认构造函数
* 则使用默认策略来创建bean对象
*/
return instantiateBean(beanName, mbd);
}
然后到instantiateBean。
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction
然后就是在initBeanWrapper中注册类型转换器,将特定类型的值转换为某一种期望的类型以及注册定制化的属性编辑器。
protected void initBeanWrapper(BeanWrapper bw) {
// 设置默认的类型转换器:用来将某些特定类型的值转换为某一种期望的类型,比如:将字符串的日期类型转换为Date类型的转换器
bw.setConversionService(getConversionService());
// 注册定制化的属性编辑器
registerCustomEditors(bw);
}
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 {
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)));
}
}
那这样子初始化的时候,我们的BeanWrapper就包含有了我们自定义的和系统默认的属性编辑器了,那有了这些属性编辑器之后我们是什么时候进行调用对应的转换方法,其实应该是在属性赋值的时候,我们具体看下populateBean的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 original;
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
// 判断属性值是否需要进行类型转换
if (mpvs.isConverted()) {
// Shortcut: use the pre-converted values as-is.
try {
/** 直接进行属性值的填充,反射调用setter方法进行赋值. */
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 valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// Create a deep copy, resolving any references for values.
List 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);
// 转换之后的属性值。可以通过自定义ConversionService进行属性值的转换
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
// 如果需要进行属性值的转换,则通过自定义的属性编辑器CustomerEditor进行属性值的转换
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();
}
try {
// 设置属性值.
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
@Nullable
private Object convertForProperty(
@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
if (converter instanceof BeanWrapperImpl) {
// 执行自定义editor
return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
}
else {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
}
}
@Nullable
public Object convertForProperty(@Nullable Object value, String propertyName) throws TypeMismatchException {
CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
"No property '" + propertyName + "' found");
}
TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
if (td == null) {
td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd)));
}
// 执行自定义editor
return convertForProperty(propertyName, null, value, td);
}
@Nullable
protected Object convertForProperty(
String propertyName, @Nullable Object oldValue, @Nullable Object newValue, TypeDescriptor td)
throws TypeMismatchException {
// 执行自定义editor
return convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
}
@Nullable
private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue,
@Nullable Object newValue, @Nullable Class> requiredType, @Nullable TypeDescriptor td)
throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
// 执行自定义editor
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
}
catch (ConverterNotFoundException | IllegalStateException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(getRootInstance(), this.nestedPath + propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, requiredType, ex);
}
catch (ConversionException | IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(getRootInstance(), this.nestedPath + propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
}
}
@Nullable
public T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType);
}
// 执行自定义editor
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
if (Object.class == requiredType) {
return (T) convertedValue;
}
else if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor strCtor = requiredType.getConstructor(String.class);
return BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
}
}
}
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && trimmedValue.isEmpty()) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass(
(Number) convertedValue, (Class) requiredType);
standardConversion = true;
}
}
else {
// convertedValue == null
if (requiredType == Optional.class) {
convertedValue = Optional.empty();
}
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (conversionAttemptEx != null) {
// Original exception from former ConversionService call above...
throw conversionAttemptEx;
}
else if (conversionService != null && typeDescriptor != null) {
// ConversionService not tried before, probably custom editor found
// but editor couldn't produce the required type...
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
}
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type '").append(
ClassUtils.getDescriptiveType(convertedValue)).append("'");
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
if (conversionAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
throw conversionAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
}
return (T) convertedValue;
}
@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 strippedPaths = new ArrayList<>();
addStrippedPropertyPaths(strippedPaths, "", propertyPath);
for (Iterator 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);
}
@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> 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;
}
@Nullable
private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class> requiredType, @Nullable PropertyEditor editor) {
Object convertedValue = newValue;
if (editor != null && !(convertedValue instanceof String)) {
// Not a String -> use PropertyEditor's setValue.
// With standard PropertyEditors, this will return the very same object;
// we just want to allow special PropertyEditors to override setValue
// for type conversion from non-String values to the required type.
try {
editor.setValue(convertedValue);
Object newConvertedValue = editor.getValue();
if (newConvertedValue != convertedValue) {
convertedValue = newConvertedValue;
// Reset PropertyEditor: It already did a proper conversion.
// Don't use it again for a setAsText call.
editor = null;
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
}
// Swallow and proceed.
}
}
Object returnValue = convertedValue;
if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
// Convert String array to a comma-separated String.
// Only applies if no PropertyEditor converted the String array before.
// The CSV String will be passed into a PropertyEditor's setAsText method, if any.
if (logger.isTraceEnabled()) {
logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
}
convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
}
if (convertedValue instanceof String) {
if (editor != null) {
// Use PropertyEditor's setAsText in case of a String value.
if (logger.isTraceEnabled()) {
logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
}
String newTextValue = (String) convertedValue;
// 自定义Edit
return doConvertTextValue(oldValue, newTextValue, editor);
}
else if (String.class == requiredType) {
returnValue = convertedValue;
}
}
return returnValue;
}
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();
}
上面就是Spring属性编辑器如何调用我们自定义的属性编辑器,先去找到对应的属性编辑器到最后的调用,这一块的源码藏的比较的深,大家可以自己debug跟着进来看看。
好了最后我们就要来实现我们自定义的属性编辑了。
首先我们有一个address跟一个customer类,如下:
public class Address {
private String district;
private String city;
private String province;
public String getDistrict() {
return district;
}
public void setDistrict(String district) {
this.district = district;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String toString() {
return this.province + "省" + this.city + "市" + this.district + "区";
}
}
public class Customer {
private String name;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
接着有一个属性编辑器,继承我们ProperEditorSupport接口。
/**
* @author maoqichuan
* @date 2022年02月21日 16:11
*/
public class AddressProperEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
String[] adds = text.split("-");
Address address = new Address();
address.setProvince(adds[0]);
address.setCity(adds[1]);
address.setDistrict(adds[2]);
this.setValue(address);
} catch (Exception e) {
e.printStackTrace();
}
}
}
有了自定义的属性编辑器之后我们仍然需要一个注册器,如下:
/**
* @author maoqichuan
* @date 2022年02月21日 16:17
*/
public class AddressProperEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Address.class,new AddressProperEditor());
}
}
最后就是propertyEditor.xml文件。