Relax singleton enforcement for Bean Overrides in the TestContext framework
In gh-33602, we introduced strict singleton enforcement for bean overrides -- for example, for @MockitoBean, @TestBean, etc. However, the use of BeanFactory#isSingleton(beanName) can result in a BeanCreationException for certain beans, such as a Spring Data JPA FactoryBean for a JpaRepository. In light of that, this commit relaxes the singleton enforcement in BeanOverrideBeanFactoryPostProcessor by only checking the result of BeanDefinition#isSingleton() for existing bean definitions. This commit also updates the Javadoc and reference documentation to reflect the status quo. See gh-33602 Closes gh-33800
This commit is contained in:
@@ -197,8 +197,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
|
||||
// Now we have an instance (the override) that we can manually register as a singleton.
|
||||
//
|
||||
// However, we need to remove any existing singleton instance -- for example, a
|
||||
// manually registered singleton or a singleton that was registered as a side effect
|
||||
// of the isSingleton() check in validateBeanDefinition().
|
||||
// manually registered singleton.
|
||||
//
|
||||
// As a bonus, by manually registering a singleton during "AOT processing", we allow
|
||||
// GenericApplicationContext's preDetermineBeanType() method to transparently register
|
||||
@@ -334,10 +333,18 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
|
||||
/**
|
||||
* Validate that the {@link BeanDefinition} for the supplied bean name is suitable
|
||||
* for being replaced by a bean override.
|
||||
* <p>If there is no registered {@code BeanDefinition} for the supplied bean name,
|
||||
* no validation is performed.
|
||||
*/
|
||||
private static void validateBeanDefinition(ConfigurableListableBeanFactory beanFactory, String beanName) {
|
||||
Assert.state(beanFactory.isSingleton(beanName),
|
||||
() -> "Unable to override bean '" + beanName + "': only singleton beans can be overridden.");
|
||||
// Due to https://github.com/spring-projects/spring-framework/issues/33800, we do NOT invoke
|
||||
// beanFactory.isSingleton(beanName), since doing so can result in a BeanCreationException for
|
||||
// certain beans -- for example, a Spring Data FactoryBean for a JpaRepository.
|
||||
if (beanFactory.containsBeanDefinition(beanName)) {
|
||||
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
|
||||
Assert.state(beanDefinition.isSingleton(),
|
||||
() -> "Unable to override bean '" + beanName + "': only singleton beans can be overridden.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void destroySingleton(ConfigurableListableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
@@ -98,7 +98,10 @@ import org.springframework.test.context.bean.override.BeanOverride;
|
||||
* }</code></pre>
|
||||
*
|
||||
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be overridden.
|
||||
* Any attempt to override a non-singleton bean will result in an exception.
|
||||
* Any attempt to override a non-singleton bean will result in an exception. When
|
||||
* overriding a bean created by a {@link org.springframework.beans.factory.FactoryBean
|
||||
* FactoryBean}, the {@code FactoryBean} will be replaced with a singleton bean
|
||||
* corresponding to the value returned from the {@code @TestBean} factory method.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
* @author Stephane Nicoll
|
||||
|
||||
@@ -52,8 +52,11 @@ import org.springframework.test.context.bean.override.BeanOverride;
|
||||
* registered directly}) will not be found, and a mocked bean will be added to
|
||||
* the context alongside the existing dependency.
|
||||
*
|
||||
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be overridden.
|
||||
* Any attempt to mock a non-singleton bean will result in an exception.
|
||||
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be mocked.
|
||||
* Any attempt to mock a non-singleton bean will result in an exception. When
|
||||
* mocking a bean created by a {@link org.springframework.beans.factory.FactoryBean
|
||||
* FactoryBean}, the {@code FactoryBean} will be replaced with a singleton mock
|
||||
* of the type of object created by the {@code FactoryBean}.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
* @author Sam Brannen
|
||||
|
||||
@@ -43,8 +43,11 @@ import org.springframework.test.context.bean.override.BeanOverride;
|
||||
* {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object)
|
||||
* registered directly} as resolvable dependencies.
|
||||
*
|
||||
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be spied.
|
||||
* Any attempt to create a spy for a non-singleton bean will result in an exception.
|
||||
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be spied. Any attempt
|
||||
* to create a spy for a non-singleton bean will result in an exception. When
|
||||
* creating a spy for a {@link org.springframework.beans.factory.FactoryBean FactoryBean},
|
||||
* a spy will be created for the object created by the {@code FactoryBean}, not
|
||||
* for the {@code FactoryBean} itself.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
* @author Sam Brannen
|
||||
|
||||
Reference in New Issue
Block a user