From 7a74e459464deae786e79aa9a977068a37bc5b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 25 Mar 2024 09:53:18 +0100 Subject: [PATCH] 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 --- .../ROOT/pages/core/beans/definition.adoc | 16 +++++ .../support/DefaultListableBeanFactory.java | 64 ++++++++++++------- .../DefaultListableBeanFactoryTests.java | 6 ++ .../annotation/LookupAnnotationTests.java | 46 +++++++------ .../factory/xml/DuplicateBeanIdTests.java | 3 +- .../factory/xml/NestedBeansElementTests.java | 1 + .../config/JCacheCustomInterceptorTests.java | 46 +++++++++---- .../cache/config/CustomInterceptorTests.java | 39 ++++++++--- .../BeanMethodPolymorphismTests.java | 5 +- .../ClassPathBeanDefinitionScannerTests.java | 1 + .../ConfigurationClassPostProcessorTests.java | 8 +++ .../EnableAspectJAutoProxyTests.java | 5 +- .../NestedConfigurationClassTests.java | 8 ++- .../ImportAnnotationDetectionTests.java | 1 + .../annotation/configuration/ImportTests.java | 2 + .../annotation/EnableAsyncTests.java | 2 + ...rceSpringJUnit4ClassRunnerAppCtxTests.java | 4 +- 17 files changed, 186 insertions(+), 71 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/core/beans/definition.adoc b/framework-docs/modules/ROOT/pages/core/beans/definition.adoc index 2141abd706f..ca6730e4074 100644 --- a/framework-docs/modules/ROOT/pages/core/beans/definition.adoc +++ b/framework-docs/modules/ROOT/pages/core/beans/definition.adoc @@ -73,7 +73,23 @@ runtime (concurrently with live access to the factory) is not officially support lead to concurrent access exceptions, inconsistent state in the bean container, or both. ==== +[[beans-definition-overriding]] +== Overriding Beans +Bean overriding is happening when a bean is registered using an identifier that is +already allocated. While bean overriding is possible, it makes the configuration harder +to read and this feature will be deprecated in a future release. + +To disable bean overriding altogether, you can set the `allowBeanDefinitionOverriding` +to `false` on the `ApplicationContext` before it is refreshed. In such setup, an +exception is thrown if bean overriding is used. + +By default, the container logs every bean overriding at `INFO` level so that you can +adapt your configuration accordingly. While not recommended, you can silence those logs +by setting the `allowBeanDefinitionOverriding` flag to `true`. + +NOTE: We acknowledge that overriding beans in a test is convenient, and there is +explicit support for this. For more details please refer to xref:testing/annotations/integration-spring/annotation-beanoverriding.adoc[this section]. [[beans-beanname]] == Naming Beans diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 32fecaa40da..384c2cbc49a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -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"); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index a41a70e236b..80fe11af274 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -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); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java index f12e9a0fac6..6c058db6ca7 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java @@ -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 { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java index 7ee14248ae4..8544568823f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java @@ -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 diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/NestedBeansElementTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/NestedBeansElementTests.java index 44cccf0b72f..38e90b31810 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/NestedBeansElementTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/NestedBeansElementTests.java @@ -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); diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheCustomInterceptorTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheCustomInterceptorTests.java index 27384ef73e5..8cc2254b155 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheCustomInterceptorTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheCustomInterceptorTests.java @@ -24,6 +24,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; @@ -43,6 +46,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatRuntimeException; /** + * Tests that use a custom {@link JCacheInterceptor}. + * * @author Stephane Nicoll */ class JCacheCustomInterceptorTests { @@ -56,16 +61,19 @@ class JCacheCustomInterceptorTests { @BeforeEach void setup() { - ctx = new AnnotationConfigApplicationContext(EnableCachingConfig.class); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.getBeanFactory().addBeanPostProcessor( + new CacheInterceptorBeanPostProcessor(context.getBeanFactory())); + context.register(EnableCachingConfig.class); + context.refresh(); + this.ctx = context; cs = ctx.getBean("service", JCacheableService.class); exceptionCache = ctx.getBean("exceptionCache", Cache.class); } @AfterEach void tearDown() { - if (ctx != null) { - ctx.close(); - } + ctx.close(); } @@ -87,8 +95,8 @@ class JCacheCustomInterceptorTests { @Test void customInterceptorAppliesWithCheckedException() { assertThatRuntimeException() - .isThrownBy(() -> cs.cacheWithCheckedException("id", true)) - .withCauseExactlyInstanceOf(IOException.class); + .isThrownBy(() -> cs.cacheWithCheckedException("id", true)) + .withCauseExactlyInstanceOf(IOException.class); } @@ -120,18 +128,28 @@ class JCacheCustomInterceptorTests { return new ConcurrentMapCache("exception"); } - @Bean - public JCacheInterceptor jCacheInterceptor(JCacheOperationSource cacheOperationSource) { - JCacheInterceptor cacheInterceptor = new TestCacheInterceptor(); - cacheInterceptor.setCacheOperationSource(cacheOperationSource); - return cacheInterceptor; - } } + static class CacheInterceptorBeanPostProcessor implements BeanPostProcessor { + + private final BeanFactory beanFactory; + + CacheInterceptorBeanPostProcessor(BeanFactory beanFactory) {this.beanFactory = beanFactory;} + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (beanName.equals("jCacheInterceptor")) { + JCacheInterceptor cacheInterceptor = new TestCacheInterceptor(); + cacheInterceptor.setCacheOperationSource(beanFactory.getBean(JCacheOperationSource.class)); + return cacheInterceptor; + } + return bean; + } + + } + /** - * A test {@link org.springframework.cache.interceptor.CacheInterceptor} that handles special exception - * types. + * A test {@link JCacheInterceptor} that handles special exception types. */ @SuppressWarnings("serial") static class TestCacheInterceptor extends JCacheInterceptor { diff --git a/spring-context/src/test/java/org/springframework/cache/config/CustomInterceptorTests.java b/spring-context/src/test/java/org/springframework/cache/config/CustomInterceptorTests.java index c3a84002e58..9421d5a1cd5 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/CustomInterceptorTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/CustomInterceptorTests.java @@ -23,6 +23,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.CacheInterceptor; @@ -40,6 +43,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; /** + * Tests that use a custom {@link CacheInterceptor}. + * * @author Stephane Nicoll */ class CustomInterceptorTests { @@ -50,7 +55,12 @@ class CustomInterceptorTests { @BeforeEach void setup() { - this.ctx = new AnnotationConfigApplicationContext(EnableCachingConfig.class); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.getBeanFactory().addBeanPostProcessor( + new CacheInterceptorBeanPostProcessor(context.getBeanFactory())); + context.register(EnableCachingConfig.class); + context.refresh(); + this.ctx = context; this.cs = ctx.getBean("service", CacheableService.class); } @@ -59,6 +69,7 @@ class CustomInterceptorTests { this.ctx.close(); } + @Test void onlyOneInterceptorIsAvailable() { Map interceptors = this.ctx.getBeansOfType(CacheInterceptor.class); @@ -96,18 +107,28 @@ class CustomInterceptorTests { return new DefaultCacheableService(); } - @Bean - public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) { - CacheInterceptor cacheInterceptor = new TestCacheInterceptor(); - cacheInterceptor.setCacheManager(cacheManager()); - cacheInterceptor.setCacheOperationSources(cacheOperationSource); - return cacheInterceptor; + } + + static class CacheInterceptorBeanPostProcessor implements BeanPostProcessor { + + private final BeanFactory beanFactory; + + CacheInterceptorBeanPostProcessor(BeanFactory beanFactory) {this.beanFactory = beanFactory;} + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (beanName.equals("cacheInterceptor")) { + CacheInterceptor cacheInterceptor = new TestCacheInterceptor(); + cacheInterceptor.setCacheManager(beanFactory.getBean(CacheManager.class)); + cacheInterceptor.setCacheOperationSource(beanFactory.getBean(CacheOperationSource.class)); + return cacheInterceptor; + } + return bean; } + } /** - * A test {@link CacheInterceptor} that handles special exception - * types. + * A test {@link CacheInterceptor} that handles special exception types. */ @SuppressWarnings("serial") static class TestCacheInterceptor extends CacheInterceptor { diff --git a/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java b/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java index 9a9ce4c6394..1b89d020bbc 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java @@ -197,7 +197,10 @@ public class BeanMethodPolymorphismTests { */ @Test void beanMethodShadowing() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ShadowConfig.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); + ctx.register(ShadowConfig.class); + ctx.refresh(); assertThat(ctx.getBean(String.class)).isEqualTo("shadow"); } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java index dbc346cb315..329804f2aba 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java @@ -194,6 +194,7 @@ class ClassPathBeanDefinitionScannerTests { @Test void testSimpleScanWithDefaultFiltersAndOverridingBean() { GenericApplicationContext context = new GenericApplicationContext(); + context.setAllowBeanDefinitionOverriding(true); context.registerBeanDefinition("stubFooDao", new RootBeanDefinition(TestBean.class)); ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context); scanner.setIncludeAnnotationConfig(false); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index c205ac781ab..ef810f56a5f 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -329,6 +329,7 @@ class ConfigurationClassPostProcessorTests { @Test void postProcessorOverridesNonApplicationBeanDefinitions() { + beanFactory.setAllowBeanDefinitionOverriding(true); RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); rbd.setRole(RootBeanDefinition.ROLE_SUPPORT); beanFactory.registerBeanDefinition("bar", rbd); @@ -342,6 +343,7 @@ class ConfigurationClassPostProcessorTests { @Test void postProcessorDoesNotOverrideRegularBeanDefinitions() { + beanFactory.setAllowBeanDefinitionOverriding(true); RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); rbd.setResource(new DescriptiveResource("XML or something")); beanFactory.registerBeanDefinition("bar", rbd); @@ -354,6 +356,7 @@ class ConfigurationClassPostProcessorTests { @Test void postProcessorDoesNotOverrideRegularBeanDefinitionsEvenWithScopedProxy() { + beanFactory.setAllowBeanDefinitionOverriding(true); RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); rbd.setResource(new DescriptiveResource("XML or something")); BeanDefinitionHolder proxied = ScopedProxyUtils.createScopedProxy( @@ -396,6 +399,7 @@ class ConfigurationClassPostProcessorTests { @Test void configurationClassesProcessedInCorrectOrder() { + beanFactory.setAllowBeanDefinitionOverriding(true); beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(OverridingSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); @@ -410,6 +414,7 @@ class ConfigurationClassPostProcessorTests { @Test void configurationClassesWithValidOverridingForProgrammaticCall() { + beanFactory.setAllowBeanDefinitionOverriding(true); beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(OverridingAgainSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(OverridingSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config3", new RootBeanDefinition(SingletonBeanConfig.class)); @@ -425,6 +430,7 @@ class ConfigurationClassPostProcessorTests { @Test void configurationClassesWithInvalidOverridingForProgrammaticCall() { + beanFactory.setAllowBeanDefinitionOverriding(true); beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(InvalidOverridingSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(OverridingSingletonBeanConfig.class)); beanFactory.registerBeanDefinition("config3", new RootBeanDefinition(SingletonBeanConfig.class)); @@ -441,6 +447,7 @@ class ConfigurationClassPostProcessorTests { @Test // SPR-15384 void nestedConfigurationClassesProcessedInCorrectOrder() { + beanFactory.setAllowBeanDefinitionOverriding(true); beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); @@ -454,6 +461,7 @@ class ConfigurationClassPostProcessorTests { @Test // SPR-16734 void innerConfigurationClassesProcessedInCorrectOrder() { + beanFactory.setAllowBeanDefinitionOverriding(true); beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedInnerClasses.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/EnableAspectJAutoProxyTests.java b/spring-context/src/test/java/org/springframework/context/annotation/EnableAspectJAutoProxyTests.java index 261663e6164..953bb98e174 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/EnableAspectJAutoProxyTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/EnableAspectJAutoProxyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 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. @@ -23,6 +23,7 @@ import example.scannable.FooDao; import example.scannable.FooService; import example.scannable.FooServiceImpl; import example.scannable.ServiceInvocationCounter; +import example.scannable.StubFooDao; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.junit.jupiter.api.Test; @@ -123,7 +124,7 @@ class EnableAspectJAutoProxyTests { } - @ComponentScan("example.scannable") + @Import({ ServiceInvocationCounter.class, StubFooDao.class }) @EnableAspectJAutoProxy(exposeProxy = true) static class ConfigWithExposedProxy { diff --git a/spring-context/src/test/java/org/springframework/context/annotation/NestedConfigurationClassTests.java b/spring-context/src/test/java/org/springframework/context/annotation/NestedConfigurationClassTests.java index 6e069c3a47b..f961a357715 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/NestedConfigurationClassTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/NestedConfigurationClassTests.java @@ -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. @@ -36,6 +36,7 @@ class NestedConfigurationClassTests { @Test void oneLevelDeep() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(L0Config.L1Config.class); ctx.refresh(); @@ -55,6 +56,7 @@ class NestedConfigurationClassTests { @Test void twoLevelsDeep() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(L0Config.class); ctx.refresh(); @@ -78,6 +80,7 @@ class NestedConfigurationClassTests { @Test void twoLevelsInLiteMode() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(L0ConfigLight.class); ctx.refresh(); @@ -101,6 +104,7 @@ class NestedConfigurationClassTests { @Test void twoLevelsDeepWithInheritance() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(S1Config.class); ctx.refresh(); @@ -130,6 +134,7 @@ class NestedConfigurationClassTests { @Test void twoLevelsDeepWithInheritanceThroughImport() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(S1Importer.class); ctx.refresh(); @@ -159,6 +164,7 @@ class NestedConfigurationClassTests { @Test void twoLevelsDeepWithInheritanceAndScopedProxy() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(S1ImporterWithProxy.class); ctx.refresh(); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportAnnotationDetectionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportAnnotationDetectionTests.java index 861754de3a0..5addfbee632 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportAnnotationDetectionTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportAnnotationDetectionTests.java @@ -65,6 +65,7 @@ public class ImportAnnotationDetectionTests { @Test void localImportIsProcessedLast() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(MultiMetaImportConfigWithLocalImportWithBeanOverride.class); ctx.refresh(); assertThat(ctx.containsBean("testBean1")).isTrue(); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java index d44c74bb2ed..80172e53808 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java @@ -181,6 +181,7 @@ class ImportTests { @Test void testImportAnnotationWithMultipleArgumentsResultingInOverriddenBeanDefinition() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + beanFactory.setAllowBeanDefinitionOverriding(true); beanFactory.registerBeanDefinition("config", new RootBeanDefinition( WithMultipleArgumentsThatWillCauseDuplication.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); @@ -380,6 +381,7 @@ class ImportTests { @Test // gh-24643 void importedConfigOverridesScanned() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.scan(SiblingImportingConfigA.class.getPackage().getName()); ctx.refresh(); diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java index 1df89b1d03d..17c3f20883e 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java @@ -96,6 +96,7 @@ class EnableAsyncTests { @Test void properExceptionForExistingProxyDependencyMismatch() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(AsyncConfig.class, AsyncBeanWithInterface.class, AsyncBeanUser.class); assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(ctx::refresh) .withCauseInstanceOf(BeanNotOfRequiredTypeException.class); @@ -105,6 +106,7 @@ class EnableAsyncTests { @Test void properExceptionForResolvedProxyDependencyMismatch() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.setAllowBeanDefinitionOverriding(true); ctx.register(AsyncConfig.class, AsyncBeanUser.class, AsyncBeanWithInterface.class); assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(ctx::refresh) .withCauseInstanceOf(BeanNotOfRequiredTypeException.class); diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.java index 2cc475b65cc..869664fe0b9 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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. @@ -31,7 +31,7 @@ import org.springframework.util.ResourceUtils; * @see AbsolutePathSpringJUnit4ClassRunnerAppCtxTests * @see RelativePathSpringJUnit4ClassRunnerAppCtxTests */ -@ContextConfiguration(locations = { ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.CLASSPATH_CONTEXT_RESOURCE_PATH }) +@ContextConfiguration(locations = { ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.CLASSPATH_CONTEXT_RESOURCE_PATH }, inheritLocations = false) public class ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests extends SpringJUnit4ClassRunnerAppCtxTests { /**