Make use of bean definition overriding more visible
This commit makes the use of bean definition overriding more visible and prepare for a deprecation of the feature in the next major release. As of this commit, use of bean definition overriding logs at INFO level. The previous log level can be restored by setting the allowBeanDefinitionOverriding flag explicitly on the BeanFactory (or via the related ApplicationContext). A number of tests that are using bean overriding on purpose have been updated to set this flag, which will make them easier to find once we actually deprecate the feature. Closes gh-31288
This commit is contained in:
@@ -152,7 +152,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
private String serializationId;
|
||||
|
||||
/** Whether to allow re-registration of a different definition with the same name. */
|
||||
private boolean allowBeanDefinitionOverriding = true;
|
||||
@Nullable
|
||||
private Boolean allowBeanDefinitionOverriding;
|
||||
|
||||
/** Whether to allow eager class loading even for lazy-init beans. */
|
||||
private boolean allowEagerClassLoading = true;
|
||||
@@ -259,7 +260,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public boolean isAllowBeanDefinitionOverriding() {
|
||||
return this.allowBeanDefinitionOverriding;
|
||||
return !Boolean.FALSE.equals(this.allowBeanDefinitionOverriding);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1142,27 +1143,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
if (!isBeanDefinitionOverridable(beanName)) {
|
||||
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
|
||||
}
|
||||
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
|
||||
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Overriding user-defined bean definition for bean '" + beanName +
|
||||
"' with a framework-generated bean definition: replacing [" +
|
||||
existingDefinition + "] with [" + beanDefinition + "]");
|
||||
}
|
||||
}
|
||||
else if (!beanDefinition.equals(existingDefinition)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Overriding bean definition for bean '" + beanName +
|
||||
"' with a different definition: replacing [" + existingDefinition +
|
||||
"] with [" + beanDefinition + "]");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Overriding bean definition for bean '" + beanName +
|
||||
"' with an equivalent definition: replacing [" + existingDefinition +
|
||||
"] with [" + beanDefinition + "]");
|
||||
}
|
||||
logBeanDefinitionOverriding(beanName, beanDefinition, existingDefinition);
|
||||
}
|
||||
this.beanDefinitionMap.put(beanName, beanDefinition);
|
||||
}
|
||||
@@ -1217,6 +1199,44 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
}
|
||||
|
||||
private void logBeanDefinitionOverriding(String beanName, BeanDefinition beanDefinition,
|
||||
BeanDefinition existingDefinition) {
|
||||
|
||||
boolean explicitBeanOverride = (this.allowBeanDefinitionOverriding != null);
|
||||
if (existingDefinition.getRole() < beanDefinition.getRole()) {
|
||||
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Overriding user-defined bean definition for bean '" + beanName +
|
||||
"' with a framework-generated bean definition: replacing [" +
|
||||
existingDefinition + "] with [" + beanDefinition + "]");
|
||||
}
|
||||
}
|
||||
else if (!beanDefinition.equals(existingDefinition)) {
|
||||
if (explicitBeanOverride && logger.isInfoEnabled()) {
|
||||
logger.info("Overriding bean definition for bean '" + beanName +
|
||||
"' with a different definition: replacing [" + existingDefinition +
|
||||
"] with [" + beanDefinition + "]");
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Overriding bean definition for bean '" + beanName +
|
||||
"' with a different definition: replacing [" + existingDefinition +
|
||||
"] with [" + beanDefinition + "]");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (explicitBeanOverride && logger.isInfoEnabled()) {
|
||||
logger.info("Overriding bean definition for bean '" + beanName +
|
||||
"' with an equivalent definition: replacing [" + existingDefinition +
|
||||
"] with [" + beanDefinition + "]");
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Overriding bean definition for bean '" + beanName +
|
||||
"' with an equivalent definition: replacing [" + existingDefinition +
|
||||
"] with [" + beanDefinition + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
|
||||
Assert.hasText(beanName, "'beanName' must not be empty");
|
||||
|
||||
@@ -840,6 +840,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
@Test
|
||||
void aliasCircle() {
|
||||
lbf.setAllowBeanDefinitionOverriding(true);
|
||||
lbf.registerAlias("test", "test2");
|
||||
lbf.registerAlias("test2", "test3");
|
||||
|
||||
@@ -867,6 +868,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
@Test
|
||||
void beanDefinitionOverriding() {
|
||||
lbf.setAllowBeanDefinitionOverriding(true);
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class));
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(NestedTestBean.class));
|
||||
lbf.registerAlias("otherTest", "test2");
|
||||
@@ -906,6 +908,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
@Test
|
||||
void beanDefinitionOverridingWithAlias() {
|
||||
lbf.setAllowBeanDefinitionOverriding(true);
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class));
|
||||
lbf.registerAlias("test", "testAlias");
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(NestedTestBean.class));
|
||||
@@ -917,6 +920,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
@Test
|
||||
void beanDefinitionOverridingWithConstructorArgumentMismatch() {
|
||||
lbf.setAllowBeanDefinitionOverriding(true);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(NestedTestBean.class);
|
||||
bd1.getConstructorArgumentValues().addIndexedArgumentValue(1, "value1");
|
||||
lbf.registerBeanDefinition("test", bd1);
|
||||
@@ -1196,6 +1200,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
@Test
|
||||
void reregisterBeanDefinition() {
|
||||
lbf.setAllowBeanDefinitionOverriding(true);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
|
||||
bd1.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
lbf.registerBeanDefinition("testBean", bd1);
|
||||
@@ -1306,6 +1311,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
@Test
|
||||
void withOverloadedSetters() {
|
||||
lbf.setAllowBeanDefinitionOverriding(true);
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(SetterOverload.class);
|
||||
rbd.getPropertyValues().add("object", "a String");
|
||||
lbf.registerBeanDefinition("overloaded", rbd);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.beans.factory.annotation;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -33,25 +32,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*/
|
||||
class LookupAnnotationTests {
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
beanFactory = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor aabpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
aabpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(aabpp);
|
||||
beanFactory.registerBeanDefinition("abstractBean", new RootBeanDefinition(AbstractBean.class));
|
||||
beanFactory.registerBeanDefinition("beanConsumer", new RootBeanDefinition(BeanConsumer.class));
|
||||
RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class);
|
||||
tbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
beanFactory.registerBeanDefinition("testBean", tbd);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testWithoutConstructorArg() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||
Object expected = bean.get();
|
||||
assertThat(expected.getClass()).isEqualTo(TestBean.class);
|
||||
@@ -60,6 +43,7 @@ class LookupAnnotationTests {
|
||||
|
||||
@Test
|
||||
void testWithOverloadedArg() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||
TestBean expected = bean.get("haha");
|
||||
assertThat(expected.getClass()).isEqualTo(TestBean.class);
|
||||
@@ -69,6 +53,7 @@ class LookupAnnotationTests {
|
||||
|
||||
@Test
|
||||
void testWithOneConstructorArg() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||
TestBean expected = bean.getOneArgument("haha");
|
||||
assertThat(expected.getClass()).isEqualTo(TestBean.class);
|
||||
@@ -78,6 +63,7 @@ class LookupAnnotationTests {
|
||||
|
||||
@Test
|
||||
void testWithTwoConstructorArg() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||
TestBean expected = bean.getTwoArguments("haha", 72);
|
||||
assertThat(expected.getClass()).isEqualTo(TestBean.class);
|
||||
@@ -88,6 +74,7 @@ class LookupAnnotationTests {
|
||||
|
||||
@Test
|
||||
void testWithThreeArgsShouldFail() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||
assertThatExceptionOfType(AbstractMethodError.class).as("TestBean has no three arg constructor").isThrownBy(() ->
|
||||
bean.getThreeArguments("name", 1, 2));
|
||||
@@ -96,6 +83,7 @@ class LookupAnnotationTests {
|
||||
|
||||
@Test
|
||||
void testWithEarlyInjection() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
AbstractBean bean = beanFactory.getBean("beanConsumer", BeanConsumer.class).abstractBean;
|
||||
Object expected = bean.get();
|
||||
assertThat(expected.getClass()).isEqualTo(TestBean.class);
|
||||
@@ -106,7 +94,7 @@ class LookupAnnotationTests {
|
||||
public void testWithNullBean() {
|
||||
RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class, () -> null);
|
||||
tbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
beanFactory.registerBeanDefinition("testBean", tbd);
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory(tbd);
|
||||
|
||||
AbstractBean bean = beanFactory.getBean("beanConsumer", BeanConsumer.class).abstractBean;
|
||||
Object expected = bean.get();
|
||||
@@ -116,6 +104,7 @@ class LookupAnnotationTests {
|
||||
|
||||
@Test
|
||||
void testWithGenericBean() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
beanFactory.registerBeanDefinition("numberBean", new RootBeanDefinition(NumberBean.class));
|
||||
beanFactory.registerBeanDefinition("doubleStore", new RootBeanDefinition(DoubleStore.class));
|
||||
beanFactory.registerBeanDefinition("floatStore", new RootBeanDefinition(FloatStore.class));
|
||||
@@ -127,6 +116,7 @@ class LookupAnnotationTests {
|
||||
|
||||
@Test
|
||||
void testSingletonWithoutMetadataCaching() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
beanFactory.setCacheBeanMetadata(false);
|
||||
|
||||
beanFactory.registerBeanDefinition("numberBean", new RootBeanDefinition(NumberBean.class));
|
||||
@@ -140,6 +130,7 @@ class LookupAnnotationTests {
|
||||
|
||||
@Test
|
||||
void testPrototypeWithoutMetadataCaching() {
|
||||
DefaultListableBeanFactory beanFactory = configureBeanFactory();
|
||||
beanFactory.setCacheBeanMetadata(false);
|
||||
|
||||
beanFactory.registerBeanDefinition("numberBean", new RootBeanDefinition(NumberBean.class, BeanDefinition.SCOPE_PROTOTYPE, null));
|
||||
@@ -155,6 +146,23 @@ class LookupAnnotationTests {
|
||||
assertThat(beanFactory.getBean(FloatStore.class)).isSameAs(bean.getFloatStore());
|
||||
}
|
||||
|
||||
private DefaultListableBeanFactory configureBeanFactory(RootBeanDefinition tbd) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor aabpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
aabpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(aabpp);
|
||||
beanFactory.registerBeanDefinition("abstractBean", new RootBeanDefinition(AbstractBean.class));
|
||||
beanFactory.registerBeanDefinition("beanConsumer", new RootBeanDefinition(BeanConsumer.class));
|
||||
beanFactory.registerBeanDefinition("testBean", tbd);
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
private DefaultListableBeanFactory configureBeanFactory() {
|
||||
RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class);
|
||||
tbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
return configureBeanFactory(tbd);
|
||||
}
|
||||
|
||||
|
||||
public abstract static class AbstractBean {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -52,6 +52,7 @@ class DuplicateBeanIdTests {
|
||||
@Test
|
||||
void duplicateBeanIdsAcrossNestingLevels() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAllowBeanDefinitionOverriding(true);
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bf);
|
||||
reader.loadBeanDefinitions(new ClassPathResource("DuplicateBeanIdTests-multiLevel-context.xml", this.getClass()));
|
||||
TestBean testBean = bf.getBean(TestBean.class); // there should be only one
|
||||
|
||||
@@ -51,6 +51,7 @@ class NestedBeansElementTests {
|
||||
env.setActiveProfiles("dev");
|
||||
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAllowBeanDefinitionOverriding(true);
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bf);
|
||||
reader.setEnvironment(env);
|
||||
reader.loadBeanDefinitions(XML);
|
||||
|
||||
Reference in New Issue
Block a user