Skip to content

Commit 83ab717

Browse files
committed
Merge branch '6.2.x'
# Conflicts: # spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java
2 parents dfe6dc1 + cda7e98 commit 83ab717

File tree

5 files changed

+108
-27
lines changed

5 files changed

+108
-27
lines changed

spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrar.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -45,4 +45,18 @@ public interface PropertyEditorRegistrar {
4545
*/
4646
void registerCustomEditors(PropertyEditorRegistry registry);
4747

48+
/**
49+
* Indicate whether this registrar exclusively overrides default editors
50+
* rather than registering custom editors, intended to be applied lazily.
51+
* <p>This has an impact on registrar handling in a bean factory: see
52+
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory#addPropertyEditorRegistrar}.
53+
* @since 6.2.3
54+
* @see PropertyEditorRegistry#registerCustomEditor
55+
* @see PropertyEditorRegistrySupport#overrideDefaultEditor
56+
* @see PropertyEditorRegistrySupport#setDefaultEditorRegistrar
57+
*/
58+
default boolean overridesDefaultEditors() {
59+
return false;
60+
}
61+
4862
}

spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -98,6 +98,8 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
9898

9999
private boolean configValueEditorsActive = false;
100100

101+
private @Nullable PropertyEditorRegistrar defaultEditorRegistrar;
102+
101103
@SuppressWarnings("NullAway.Init")
102104
private Map<Class<?>, PropertyEditor> defaultEditors;
103105

@@ -149,6 +151,19 @@ public void useConfigValueEditors() {
149151
this.configValueEditorsActive = true;
150152
}
151153

154+
/**
155+
* Set a registrar for default editors, as a lazy way of overriding default editors.
156+
* <p>This is expected to be a collaborator with {@link PropertyEditorRegistrySupport},
157+
* downcasting the given {@link PropertyEditorRegistry} accordingly and calling
158+
* {@link #overrideDefaultEditor} for registering additional default editors on it.
159+
* @param registrar the registrar to call when default editors are actually needed
160+
* @since 6.2.3
161+
* @see #overrideDefaultEditor
162+
*/
163+
public void setDefaultEditorRegistrar(PropertyEditorRegistrar registrar) {
164+
this.defaultEditorRegistrar = registrar;
165+
}
166+
152167
/**
153168
* Override the default editor for the specified type with the given property editor.
154169
* <p>Note that this is different from registering a custom editor in that the editor
@@ -176,6 +191,9 @@ public void overrideDefaultEditor(Class<?> requiredType, PropertyEditor property
176191
if (!this.defaultEditorsActive) {
177192
return null;
178193
}
194+
if (this.overriddenDefaultEditors == null && this.defaultEditorRegistrar != null) {
195+
this.defaultEditorRegistrar.registerCustomEditors(this);
196+
}
179197
if (this.overriddenDefaultEditors != null) {
180198
PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
181199
if (editor != null) {

spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -179,7 +179,11 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
179179
* on the given registry, fresh for each bean creation attempt. This avoids
180180
* the need for synchronization on custom editors; hence, it is generally
181181
* preferable to use this method instead of {@link #registerCustomEditor}.
182+
* <p>If the given registrar implements
183+
* {@link PropertyEditorRegistrar#overridesDefaultEditors()} to return {@code true},
184+
* it will be applied lazily (only when default editors are actually needed).
182185
* @param registrar the PropertyEditorRegistrar to register
186+
* @see PropertyEditorRegistrar#overridesDefaultEditors()
183187
*/
184188
void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
185189

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java

+60-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -133,6 +133,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
133133
/** Spring ConversionService to use instead of PropertyEditors. */
134134
private @Nullable ConversionService conversionService;
135135

136+
/** Default PropertyEditorRegistrars to apply to the beans of this factory. */
137+
private final Set<PropertyEditorRegistrar> defaultEditorRegistrars = new LinkedHashSet<>(4);
138+
136139
/** Custom PropertyEditorRegistrars to apply to the beans of this factory. */
137140
private final Set<PropertyEditorRegistrar> propertyEditorRegistrars = new LinkedHashSet<>(4);
138141

@@ -870,7 +873,12 @@ public void setConversionService(@Nullable ConversionService conversionService)
870873
@Override
871874
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
872875
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
873-
this.propertyEditorRegistrars.add(registrar);
876+
if (registrar.overridesDefaultEditors()) {
877+
this.defaultEditorRegistrars.add(registrar);
878+
}
879+
else {
880+
this.propertyEditorRegistrars.add(registrar);
881+
}
874882
}
875883

876884
/**
@@ -1098,6 +1106,7 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
10981106
setBeanExpressionResolver(otherFactory.getBeanExpressionResolver());
10991107
setConversionService(otherFactory.getConversionService());
11001108
if (otherFactory instanceof AbstractBeanFactory otherAbstractFactory) {
1109+
this.defaultEditorRegistrars.addAll(otherAbstractFactory.defaultEditorRegistrars);
11011110
this.propertyEditorRegistrars.addAll(otherAbstractFactory.propertyEditorRegistrars);
11021111
this.customEditors.putAll(otherAbstractFactory.customEditors);
11031112
this.typeConverter = otherAbstractFactory.typeConverter;
@@ -1297,36 +1306,48 @@ protected void initBeanWrapper(BeanWrapper bw) {
12971306
protected void registerCustomEditors(PropertyEditorRegistry registry) {
12981307
if (registry instanceof PropertyEditorRegistrySupport registrySupport) {
12991308
registrySupport.useConfigValueEditors();
1309+
if (!this.defaultEditorRegistrars.isEmpty()) {
1310+
// Optimization: lazy overriding of default editors only when needed
1311+
registrySupport.setDefaultEditorRegistrar(new BeanFactoryDefaultEditorRegistrar());
1312+
}
1313+
}
1314+
else if (!this.defaultEditorRegistrars.isEmpty()) {
1315+
// Fallback: proactive overriding of default editors
1316+
applyEditorRegistrars(registry, this.defaultEditorRegistrars);
13001317
}
1318+
13011319
if (!this.propertyEditorRegistrars.isEmpty()) {
1302-
for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
1303-
try {
1304-
registrar.registerCustomEditors(registry);
1305-
}
1306-
catch (BeanCreationException ex) {
1307-
Throwable rootCause = ex.getMostSpecificCause();
1308-
if (rootCause instanceof BeanCurrentlyInCreationException bce) {
1309-
String bceBeanName = bce.getBeanName();
1310-
if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
1311-
if (logger.isDebugEnabled()) {
1312-
logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
1313-
"] failed because it tried to obtain currently created bean '" +
1314-
ex.getBeanName() + "': " + ex.getMessage());
1315-
}
1316-
onSuppressedException(ex);
1317-
continue;
1318-
}
1319-
}
1320-
throw ex;
1321-
}
1322-
}
1320+
applyEditorRegistrars(registry, this.propertyEditorRegistrars);
13231321
}
13241322
if (!this.customEditors.isEmpty()) {
13251323
this.customEditors.forEach((requiredType, editorClass) ->
13261324
registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
13271325
}
13281326
}
13291327

1328+
private void applyEditorRegistrars(PropertyEditorRegistry registry, Set<PropertyEditorRegistrar> registrars) {
1329+
for (PropertyEditorRegistrar registrar : registrars) {
1330+
try {
1331+
registrar.registerCustomEditors(registry);
1332+
}
1333+
catch (BeanCreationException ex) {
1334+
Throwable rootCause = ex.getMostSpecificCause();
1335+
if (rootCause instanceof BeanCurrentlyInCreationException bce) {
1336+
String bceBeanName = bce.getBeanName();
1337+
if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
1338+
if (logger.isDebugEnabled()) {
1339+
logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
1340+
"] failed because it tried to obtain currently created bean '" +
1341+
ex.getBeanName() + "': " + ex.getMessage());
1342+
}
1343+
onSuppressedException(ex);
1344+
return;
1345+
}
1346+
}
1347+
throw ex;
1348+
}
1349+
}
1350+
}
13301351

13311352
/**
13321353
* Return a merged RootBeanDefinition, traversing the parent bean definition
@@ -2075,4 +2096,20 @@ static class BeanPostProcessorCache {
20752096
final List<MergedBeanDefinitionPostProcessor> mergedDefinition = new ArrayList<>();
20762097
}
20772098

2099+
2100+
/**
2101+
* {@link PropertyEditorRegistrar} that delegates to the bean factory's
2102+
* default registrars, adding exception handling for circular reference
2103+
* scenarios where an editor tries to refer back to the currently created bean.
2104+
*
2105+
* @since 6.2.3
2106+
*/
2107+
class BeanFactoryDefaultEditorRegistrar implements PropertyEditorRegistrar {
2108+
2109+
@Override
2110+
public void registerCustomEditors(PropertyEditorRegistry registry) {
2111+
applyEditorRegistrars(registry, defaultEditorRegistrars);
2112+
}
2113+
}
2114+
20782115
}

spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -135,4 +135,12 @@ private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> required
135135
}
136136
}
137137

138+
/**
139+
* Indicate the use of {@link PropertyEditorRegistrySupport#overrideDefaultEditor} above.
140+
*/
141+
@Override
142+
public boolean overridesDefaultEditors() {
143+
return true;
144+
}
145+
138146
}

0 commit comments

Comments
 (0)