Skip to content

Commit 8d652e9

Browse files
committed
Reinstate Bean Override support for replacing a manually registered singleton
Closes gh-33678
1 parent c70a6d3 commit 8d652e9

File tree

2 files changed

+79
-1
lines changed

2 files changed

+79
-1
lines changed

spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ private void replaceDefinition(ConfigurableListableBeanFactory beanFactory, Over
128128
if (beanName != null) {
129129
// We are overriding an existing bean by-type.
130130
beanName = BeanFactoryUtils.transformedBeanName(beanName);
131-
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
131+
// If we are overriding a manually registered singleton, we won't find
132+
// an existing bean definition.
133+
if (beanFactory.containsBeanDefinition(beanName)) {
134+
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
135+
}
132136
}
133137
else {
134138
// We will later generate a name for the nonexistent bean, but since NullAway
@@ -150,6 +154,12 @@ else if (requireExistingDefinition) {
150154
}
151155
}
152156

157+
// Ensure we don't have any manually registered singletons registered, since we
158+
// register a bean override instance as a manual singleton at the end of this method.
159+
if (beanFactory.containsSingleton(beanName)) {
160+
destroySingleton(beanFactory, beanName);
161+
}
162+
153163
if (existingBeanDefinition != null) {
154164
// Validate the existing bean definition.
155165
//
@@ -333,6 +343,10 @@ private static void validateBeanDefinition(ConfigurableListableBeanFactory beanF
333343
// Since the isSingleton() check above may have registered a singleton as a side
334344
// effect -- for example, for a FactoryBean -- we need to destroy the singleton,
335345
// because we later manually register a bean override instance as a singleton.
346+
destroySingleton(beanFactory, beanName);
347+
}
348+
349+
private static void destroySingleton(ConfigurableListableBeanFactory beanFactory, String beanName) {
336350
if (beanFactory instanceof DefaultListableBeanFactory dlbf) {
337351
dlbf.destroySingleton(beanName);
338352
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.bean.override.mockito;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.context.ApplicationContextInitializer;
22+
import org.springframework.context.ConfigurableApplicationContext;
23+
import org.springframework.test.context.bean.override.mockito.MockitoBeanManuallyRegisteredSingletonTests.SingletonRegistrar;
24+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.mockito.BDDMockito.when;
28+
29+
/**
30+
* Verifies support for overriding a manually registered singleton bean with
31+
* {@link MockitoBean @MockitoBean}.
32+
*
33+
* @author Andy Wilkinson
34+
* @author Sam Brannen
35+
* @since 6.2
36+
*/
37+
@SpringJUnitConfig(initializers = SingletonRegistrar.class)
38+
class MockitoBeanManuallyRegisteredSingletonTests {
39+
40+
@MockitoBean
41+
MessageService messageService;
42+
43+
@Test
44+
void test() {
45+
when(messageService.getMessage()).thenReturn("override");
46+
assertThat(messageService.getMessage()).isEqualTo("override");
47+
}
48+
49+
static class SingletonRegistrar implements ApplicationContextInitializer<ConfigurableApplicationContext> {
50+
51+
@Override
52+
public void initialize(ConfigurableApplicationContext applicationContext) {
53+
applicationContext.getBeanFactory().registerSingleton("messageService", new MessageService());
54+
}
55+
}
56+
57+
static class MessageService {
58+
59+
String getMessage() {
60+
return "production";
61+
}
62+
}
63+
64+
}

0 commit comments

Comments
 (0)