Lazily override default editors when actually needed
Closes gh-34361
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,4 +45,18 @@ public interface PropertyEditorRegistrar {
|
||||
*/
|
||||
void registerCustomEditors(PropertyEditorRegistry registry);
|
||||
|
||||
/**
|
||||
* Indicate whether this registrar exclusively overrides default editors
|
||||
* rather than registering custom editors, intended to be applied lazily.
|
||||
* <p>This has an impact on registrar handling in a bean factory: see
|
||||
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory#addPropertyEditorRegistrar}.
|
||||
* @since 6.2.3
|
||||
* @see PropertyEditorRegistry#registerCustomEditor
|
||||
* @see PropertyEditorRegistrySupport#overrideDefaultEditor
|
||||
* @see PropertyEditorRegistrySupport#setDefaultEditorRegistrar
|
||||
*/
|
||||
default boolean overridesDefaultEditors() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -99,6 +99,9 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
|
||||
|
||||
private boolean configValueEditorsActive = false;
|
||||
|
||||
@Nullable
|
||||
private PropertyEditorRegistrar defaultEditorRegistrar;
|
||||
|
||||
@Nullable
|
||||
private Map<Class<?>, PropertyEditor> defaultEditors;
|
||||
|
||||
@@ -155,6 +158,19 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
|
||||
this.configValueEditorsActive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a registrar for default editors, as a lazy way of overriding default editors.
|
||||
* <p>This is expected to be a collaborator with {@link PropertyEditorRegistrySupport},
|
||||
* downcasting the given {@link PropertyEditorRegistry} accordingly and calling
|
||||
* {@link #overrideDefaultEditor} for registering additional default editors on it.
|
||||
* @param registrar the registrar to call when default editors are actually needed
|
||||
* @since 6.2.3
|
||||
* @see #overrideDefaultEditor
|
||||
*/
|
||||
public void setDefaultEditorRegistrar(PropertyEditorRegistrar registrar) {
|
||||
this.defaultEditorRegistrar = registrar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -184,6 +200,9 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
|
||||
if (!this.defaultEditorsActive) {
|
||||
return null;
|
||||
}
|
||||
if (this.overriddenDefaultEditors == null && this.defaultEditorRegistrar != null) {
|
||||
this.defaultEditorRegistrar.registerCustomEditors(this);
|
||||
}
|
||||
if (this.overriddenDefaultEditors != null) {
|
||||
PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
|
||||
if (editor != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -183,7 +183,11 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
|
||||
* on the given registry, fresh for each bean creation attempt. This avoids
|
||||
* the need for synchronization on custom editors; hence, it is generally
|
||||
* preferable to use this method instead of {@link #registerCustomEditor}.
|
||||
* <p>If the given registrar implements
|
||||
* {@link PropertyEditorRegistrar#overridesDefaultEditors()} to return {@code true},
|
||||
* it will be applied lazily (only when default editors are actually needed).
|
||||
* @param registrar the PropertyEditorRegistrar to register
|
||||
* @see PropertyEditorRegistrar#overridesDefaultEditors()
|
||||
*/
|
||||
void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -137,6 +137,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Nullable
|
||||
private ConversionService conversionService;
|
||||
|
||||
/** Default PropertyEditorRegistrars to apply to the beans of this factory. */
|
||||
private final Set<PropertyEditorRegistrar> defaultEditorRegistrars = new LinkedHashSet<>(4);
|
||||
|
||||
/** Custom PropertyEditorRegistrars to apply to the beans of this factory. */
|
||||
private final Set<PropertyEditorRegistrar> propertyEditorRegistrars = new LinkedHashSet<>(4);
|
||||
|
||||
@@ -883,7 +886,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Override
|
||||
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
|
||||
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
|
||||
this.propertyEditorRegistrars.add(registrar);
|
||||
if (registrar.overridesDefaultEditors()) {
|
||||
this.defaultEditorRegistrars.add(registrar);
|
||||
}
|
||||
else {
|
||||
this.propertyEditorRegistrars.add(registrar);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1114,6 +1122,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
setBeanExpressionResolver(otherFactory.getBeanExpressionResolver());
|
||||
setConversionService(otherFactory.getConversionService());
|
||||
if (otherFactory instanceof AbstractBeanFactory otherAbstractFactory) {
|
||||
this.defaultEditorRegistrars.addAll(otherAbstractFactory.defaultEditorRegistrars);
|
||||
this.propertyEditorRegistrars.addAll(otherAbstractFactory.propertyEditorRegistrars);
|
||||
this.customEditors.putAll(otherAbstractFactory.customEditors);
|
||||
this.typeConverter = otherAbstractFactory.typeConverter;
|
||||
@@ -1313,36 +1322,48 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
protected void registerCustomEditors(PropertyEditorRegistry registry) {
|
||||
if (registry instanceof PropertyEditorRegistrySupport registrySupport) {
|
||||
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 bce) {
|
||||
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.defaultEditorRegistrars.isEmpty()) {
|
||||
// Optimization: lazy overriding of default editors only when needed
|
||||
registrySupport.setDefaultEditorRegistrar(new BeanFactoryDefaultEditorRegistrar());
|
||||
}
|
||||
}
|
||||
else if (!this.defaultEditorRegistrars.isEmpty()) {
|
||||
// Fallback: proactive overriding of default editors
|
||||
applyEditorRegistrars(registry, this.defaultEditorRegistrars);
|
||||
}
|
||||
|
||||
if (!this.propertyEditorRegistrars.isEmpty()) {
|
||||
applyEditorRegistrars(registry, this.propertyEditorRegistrars);
|
||||
}
|
||||
if (!this.customEditors.isEmpty()) {
|
||||
this.customEditors.forEach((requiredType, editorClass) ->
|
||||
registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyEditorRegistrars(PropertyEditorRegistry registry, Set<PropertyEditorRegistrar> registrars) {
|
||||
for (PropertyEditorRegistrar registrar : registrars) {
|
||||
try {
|
||||
registrar.registerCustomEditors(registry);
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
Throwable rootCause = ex.getMostSpecificCause();
|
||||
if (rootCause instanceof BeanCurrentlyInCreationException bce) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a merged RootBeanDefinition, traversing the parent bean definition
|
||||
@@ -2095,4 +2116,20 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
final List<MergedBeanDefinitionPostProcessor> mergedDefinition = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link PropertyEditorRegistrar} that delegates to the bean factory's
|
||||
* default registrars, adding exception handling for circular reference
|
||||
* scenarios where an editor tries to refer back to the currently created bean.
|
||||
*
|
||||
* @since 6.2.3
|
||||
*/
|
||||
class BeanFactoryDefaultEditorRegistrar implements PropertyEditorRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerCustomEditors(PropertyEditorRegistry registry) {
|
||||
applyEditorRegistrars(registry, defaultEditorRegistrars);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -135,4 +135,12 @@ public class ResourceEditorRegistrar implements PropertyEditorRegistrar {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the use of {@link PropertyEditorRegistrySupport#overrideDefaultEditor} above.
|
||||
*/
|
||||
@Override
|
||||
public boolean overridesDefaultEditors() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user