Skip to content

Commit 7d4118a

Browse files
committed
Align withBean methods with ApplicationContext
Rework `AbstractApplicationContextRunner.withBean` methods to align signatures as much as possible with those provided by the `ApplicationContext`. Also update the implementation to use a dedicate member variable rather than adding initializers. Closes gh-16011
1 parent 2dfd916 commit 7d4118a

File tree

6 files changed

+106
-63
lines changed

6 files changed

+106
-63
lines changed

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/flyway/FlywayEndpointTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class FlywayEndpointTests {
4141
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
4242
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
4343
.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
44-
.withBean("endpoint", FlywayEndpoint.class, FlywayEndpoint::new);
44+
.withBean("endpoint", FlywayEndpoint.class);
4545

4646
@Test
4747
public void flywayReportIsProduced() {

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunner.java

Lines changed: 93 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
import java.util.ArrayList;
2020
import java.util.Collections;
2121
import java.util.List;
22+
import java.util.function.Consumer;
2223
import java.util.function.Function;
2324
import java.util.function.Supplier;
2425

26+
import org.springframework.beans.factory.config.BeanDefinition;
27+
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
2528
import org.springframework.beans.factory.support.BeanNameGenerator;
2629
import org.springframework.boot.context.annotation.Configurations;
2730
import org.springframework.boot.context.annotation.UserConfigurations;
@@ -112,6 +115,8 @@ public abstract class AbstractApplicationContextRunner<SELF extends AbstractAppl
112115

113116
private final ApplicationContext parent;
114117

118+
private final List<BeanRegistration<?>> beanRegistrations;
119+
115120
private final List<Configurations> configurations;
116121

117122
/**
@@ -120,7 +125,8 @@ public abstract class AbstractApplicationContextRunner<SELF extends AbstractAppl
120125
*/
121126
protected AbstractApplicationContextRunner(Supplier<C> contextFactory) {
122127
this(contextFactory, Collections.emptyList(), TestPropertyValues.empty(),
123-
TestPropertyValues.empty(), null, null, Collections.emptyList());
128+
TestPropertyValues.empty(), null, null, Collections.emptyList(),
129+
Collections.emptyList());
124130
}
125131

126132
/**
@@ -131,12 +137,14 @@ protected AbstractApplicationContextRunner(Supplier<C> contextFactory) {
131137
* @param systemProperties the system properties
132138
* @param classLoader the class loader
133139
* @param parent the parent
140+
* @param beanRegistrations the bean registrations
134141
* @param configurations the configuration
135142
*/
136143
protected AbstractApplicationContextRunner(Supplier<C> contextFactory,
137144
List<ApplicationContextInitializer<? super C>> initializers,
138145
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
139146
ClassLoader classLoader, ApplicationContext parent,
147+
List<BeanRegistration<?>> beanRegistrations,
140148
List<Configurations> configurations) {
141149
Assert.notNull(contextFactory, "ContextFactory must not be null");
142150
Assert.notNull(environmentProperties, "EnvironmentProperties must not be null");
@@ -149,6 +157,7 @@ protected AbstractApplicationContextRunner(Supplier<C> contextFactory,
149157
this.systemProperties = systemProperties;
150158
this.classLoader = classLoader;
151159
this.parent = parent;
160+
this.beanRegistrations = Collections.unmodifiableList(beanRegistrations);
152161
this.configurations = Collections.unmodifiableList(configurations);
153162
}
154163

@@ -162,7 +171,7 @@ public SELF withInitializer(ApplicationContextInitializer<? super C> initializer
162171
Assert.notNull(initializer, "Initializer must not be null");
163172
return newInstance(this.contextFactory, add(this.initializers, initializer),
164173
this.environmentProperties, this.systemProperties, this.classLoader,
165-
this.parent, this.configurations);
174+
this.parent, this.beanRegistrations, this.configurations);
166175
}
167176

168177
/**
@@ -178,7 +187,8 @@ public SELF withInitializer(ApplicationContextInitializer<? super C> initializer
178187
public SELF withPropertyValues(String... pairs) {
179188
return newInstance(this.contextFactory, this.initializers,
180189
this.environmentProperties.and(pairs), this.systemProperties,
181-
this.classLoader, this.parent, this.configurations);
190+
this.classLoader, this.parent, this.beanRegistrations,
191+
this.configurations);
182192
}
183193

184194
/**
@@ -194,7 +204,8 @@ public SELF withPropertyValues(String... pairs) {
194204
public SELF withSystemProperties(String... pairs) {
195205
return newInstance(this.contextFactory, this.initializers,
196206
this.environmentProperties, this.systemProperties.and(pairs),
197-
this.classLoader, this.parent, this.configurations);
207+
this.classLoader, this.parent, this.beanRegistrations,
208+
this.configurations);
198209
}
199210

200211
/**
@@ -207,7 +218,7 @@ public SELF withSystemProperties(String... pairs) {
207218
public SELF withClassLoader(ClassLoader classLoader) {
208219
return newInstance(this.contextFactory, this.initializers,
209220
this.environmentProperties, this.systemProperties, classLoader,
210-
this.parent, this.configurations);
221+
this.parent, this.beanRegistrations, this.configurations);
211222
}
212223

213224
/**
@@ -219,7 +230,7 @@ public SELF withClassLoader(ClassLoader classLoader) {
219230
public SELF withParent(ApplicationContext parent) {
220231
return newInstance(this.contextFactory, this.initializers,
221232
this.environmentProperties, this.systemProperties, this.classLoader,
222-
parent, this.configurations);
233+
parent, this.beanRegistrations, this.configurations);
223234
}
224235

225236
/**
@@ -229,75 +240,81 @@ public SELF withParent(ApplicationContext parent) {
229240
* <p>
230241
* Such beans are registered after regular {@linkplain #withUserConfiguration(Class[])
231242
* user configurations} in the order of registration.
232-
* @param beanType the type of the bean
233-
* @param beanDefinition a supplier for the bean
243+
* @param type the type of the bean
244+
* @param constructorArgs custom argument values to be fed into Spring's constructor
245+
* resolution algorithm, resolving either all arguments or just specific ones, with
246+
* the rest to be resolved through regular autowiring (may be {@code null} or empty)
234247
* @param <T> the type of the bean
235248
* @return a new instance with the updated bean
236249
*/
237-
public <T> SELF withBean(Class<T> beanType, Supplier<T> beanDefinition) {
238-
return withBean(null, beanType, beanDefinition);
250+
public <T> SELF withBean(Class<T> type, Object... constructorArgs) {
251+
return withBean(null, type, constructorArgs);
239252
}
240253

241254
/**
242-
* Register the specified user bean with the {@link ApplicationContext}. The bean name
243-
* is generated from the configured {@link BeanNameGenerator} on the underlying
244-
* context.
255+
* Register the specified user bean with the {@link ApplicationContext}.
245256
* <p>
246257
* Such beans are registered after regular {@linkplain #withUserConfiguration(Class[])
247258
* user configurations} in the order of registration.
248-
* @param beanType the type of the bean
249-
* @param beanDefinition a function that accepts the context and return the bean
259+
* @param name the bean name or {@code null} to use a generated name
260+
* @param type the type of the bean
261+
* @param constructorArgs custom argument values to be fed into Spring's constructor
262+
* resolution algorithm, resolving either all arguments or just specific ones, with
263+
* the rest to be resolved through regular autowiring (may be {@code null} or empty)
250264
* @param <T> the type of the bean
251265
* @return a new instance with the updated bean
252266
*/
253-
public <T> SELF withBean(Class<T> beanType, Function<? super C, T> beanDefinition) {
254-
return withBean(null, beanType, beanDefinition);
267+
public <T> SELF withBean(String name, Class<T> type, Object... constructorArgs) {
268+
return newInstance(this.contextFactory, this.initializers,
269+
this.environmentProperties, this.systemProperties, this.classLoader,
270+
this.parent,
271+
add(this.beanRegistrations,
272+
new BeanRegistration<>(name, type, constructorArgs)),
273+
this.configurations);
255274
}
256275

257276
/**
258-
* Register the specified user bean with the {@link ApplicationContext}. If no bean
259-
* name is provided, a default one is generated from the configured
260-
* {@link BeanNameGenerator} on the underlying context.
277+
* Register the specified user bean with the {@link ApplicationContext}. The bean name
278+
* is generated from the configured {@link BeanNameGenerator} on the underlying
279+
* context.
261280
* <p>
262281
* Such beans are registered after regular {@linkplain #withUserConfiguration(Class[])
263282
* user configurations} in the order of registration.
264-
* @param beanName the name of the bean (may be {@code null})
265-
* @param beanType the type of the bean
266-
* @param beanDefinition a supplier for the bean
283+
* @param type the type of the bean
284+
* @param supplier a supplier for the bean
285+
* @param customizers one or more callbacks for customizing the factory's
286+
* {@link BeanDefinition}, e.g. setting a lazy-init or primary flag
267287
* @param <T> the type of the bean
268288
* @return a new instance with the updated bean
269289
*/
270-
public <T> SELF withBean(String beanName, Class<T> beanType,
271-
Supplier<T> beanDefinition) {
272-
return withBean(beanName, beanType, (context) -> beanDefinition.get());
290+
public <T> SELF withBean(Class<T> type, Supplier<T> supplier,
291+
BeanDefinitionCustomizer... customizers) {
292+
return withBean(null, type, supplier, customizers);
273293
}
274294

275295
/**
276-
* Register the specified user bean with the {@link ApplicationContext}. If no bean
277-
* name is provided, a default one is generated from the configured
278-
* {@link BeanNameGenerator} on the underlying context.
296+
* Register the specified user bean with the {@link ApplicationContext}. The bean name
297+
* is generated from the configured {@link BeanNameGenerator} on the underlying
298+
* context.
279299
* <p>
280300
* Such beans are registered after regular {@linkplain #withUserConfiguration(Class[])
281301
* user configurations} in the order of registration.
282-
* @param beanName the name of the bean (may be {@code null})
283-
* @param beanType the type of the bean
284-
* @param beanDefinition a function that accepts the context and return the bean
302+
* @param name the bean name or {@code null} to use a generated name
303+
* @param type the type of the bean
304+
* @param supplier a supplier for the bean
305+
* @param customizers one or more callbacks for customizing the factory's
306+
* {@link BeanDefinition}, e.g. setting a lazy-init or primary flag
285307
* @param <T> the type of the bean
286308
* @return a new instance with the updated bean
287309
*/
288-
public <T> SELF withBean(String beanName, Class<T> beanType,
289-
Function<? super C, T> beanDefinition) {
290-
return withInitializer(
291-
beanDefinitionRegistrar(beanName, beanType, beanDefinition));
292-
}
293-
294-
private <T> ApplicationContextInitializer<? super C> beanDefinitionRegistrar(
295-
String beanName, Class<T> beanType, Function<? super C, T> beanDefinition) {
296-
return (context) -> {
297-
Assert.isInstanceOf(GenericApplicationContext.class, context);
298-
((GenericApplicationContext) context).registerBean(beanName, beanType,
299-
() -> beanDefinition.apply(context));
300-
};
310+
public <T> SELF withBean(String name, Class<T> type, Supplier<T> supplier,
311+
BeanDefinitionCustomizer... customizers) {
312+
return newInstance(this.contextFactory, this.initializers,
313+
this.environmentProperties, this.systemProperties, this.classLoader,
314+
this.parent,
315+
add(this.beanRegistrations,
316+
new BeanRegistration<>(name, type, supplier, customizers)),
317+
this.configurations);
301318
}
302319

303320
/**
@@ -319,7 +336,8 @@ public SELF withConfiguration(Configurations configurations) {
319336
Assert.notNull(configurations, "Configurations must not be null");
320337
return newInstance(this.contextFactory, this.initializers,
321338
this.environmentProperties, this.systemProperties, this.classLoader,
322-
this.parent, add(this.configurations, configurations));
339+
this.parent, this.beanRegistrations,
340+
add(this.configurations, configurations));
323341
}
324342

325343
/**
@@ -342,6 +360,7 @@ protected abstract SELF newInstance(Supplier<C> contextFactory,
342360
List<ApplicationContextInitializer<? super C>> initializers,
343361
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
344362
ClassLoader classLoader, ApplicationContext parent,
363+
List<BeanRegistration<?>> beanRegistrations,
345364
List<Configurations> configurations);
346365

347366
/**
@@ -416,6 +435,7 @@ private void configureContext(C context) {
416435
if (classes.length > 0) {
417436
((AnnotationConfigRegistry) context).register(classes);
418437
}
438+
this.beanRegistrations.forEach((registration) -> registration.apply(context));
419439
this.initializers.forEach((initializer) -> initializer.initialize(context));
420440
context.refresh();
421441
}
@@ -434,4 +454,31 @@ private <E extends Throwable> void rethrow(Throwable e) throws E {
434454
throw (E) e;
435455
}
436456

457+
/**
458+
* A Bean registration to be applied when the context loaded.
459+
*
460+
* @param <T> the bean type
461+
*/
462+
protected final class BeanRegistration<T> {
463+
464+
Consumer<GenericApplicationContext> registrar;
465+
466+
public BeanRegistration(String name, Class<T> type, Object... constructorArgs) {
467+
this.registrar = (context) -> context.registerBean(name, type,
468+
constructorArgs);
469+
}
470+
471+
public BeanRegistration(String name, Class<T> type, Supplier<T> supplier,
472+
BeanDefinitionCustomizer... customizers) {
473+
this.registrar = (context) -> context.registerBean(name, type, supplier,
474+
customizers);
475+
}
476+
477+
public void apply(ConfigurableApplicationContext context) {
478+
Assert.isInstanceOf(GenericApplicationContext.class, context);
479+
this.registrar.accept(((GenericApplicationContext) context));
480+
}
481+
482+
}
483+
437484
}

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ApplicationContextRunner.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ private ApplicationContextRunner(
6464
List<ApplicationContextInitializer<? super ConfigurableApplicationContext>> initializers,
6565
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
6666
ClassLoader classLoader, ApplicationContext parent,
67+
List<BeanRegistration<?>> beanRegistrations,
6768
List<Configurations> configurations) {
6869
super(contextFactory, initializers, environmentProperties, systemProperties,
69-
classLoader, parent, configurations);
70+
classLoader, parent, beanRegistrations, configurations);
7071
}
7172

7273
@Override
@@ -75,10 +76,11 @@ protected ApplicationContextRunner newInstance(
7576
List<ApplicationContextInitializer<? super ConfigurableApplicationContext>> initializers,
7677
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
7778
ClassLoader classLoader, ApplicationContext parent,
79+
List<BeanRegistration<?>> beanRegistrations,
7880
List<Configurations> configurations) {
7981
return new ApplicationContextRunner(contextFactory, initializers,
8082
environmentProperties, systemProperties, classLoader, parent,
81-
configurations);
83+
beanRegistrations, configurations);
8284
}
8385

8486
}

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunner.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ private ReactiveWebApplicationContextRunner(
6464
List<ApplicationContextInitializer<? super ConfigurableReactiveWebApplicationContext>> initializers,
6565
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
6666
ClassLoader classLoader, ApplicationContext parent,
67+
List<BeanRegistration<?>> beanRegistrations,
6768
List<Configurations> configurations) {
6869
super(contextFactory, initializers, environmentProperties, systemProperties,
69-
classLoader, parent, configurations);
70+
classLoader, parent, beanRegistrations, configurations);
7071
}
7172

7273
@Override
@@ -75,10 +76,11 @@ protected ReactiveWebApplicationContextRunner newInstance(
7576
List<ApplicationContextInitializer<? super ConfigurableReactiveWebApplicationContext>> initializers,
7677
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
7778
ClassLoader classLoader, ApplicationContext parent,
79+
List<BeanRegistration<?>> beanRegistrations,
7880
List<Configurations> configurations) {
7981
return new ReactiveWebApplicationContextRunner(contextFactory, initializers,
8082
environmentProperties, systemProperties, classLoader, parent,
81-
configurations);
83+
beanRegistrations, configurations);
8284
}
8385

8486
}

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/WebApplicationContextRunner.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ private WebApplicationContextRunner(
6868
List<ApplicationContextInitializer<? super ConfigurableWebApplicationContext>> initializers,
6969
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
7070
ClassLoader classLoader, ApplicationContext parent,
71+
List<BeanRegistration<?>> beanRegistrations,
7172
List<Configurations> configurations) {
7273
super(contextFactory, initializers, environmentProperties, systemProperties,
73-
classLoader, parent, configurations);
74+
classLoader, parent, beanRegistrations, configurations);
7475
}
7576

7677
@Override
@@ -79,10 +80,11 @@ protected WebApplicationContextRunner newInstance(
7980
List<ApplicationContextInitializer<? super ConfigurableWebApplicationContext>> initializers,
8081
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
8182
ClassLoader classLoader, ApplicationContext parent,
83+
List<BeanRegistration<?>> beanRegistrations,
8284
List<Configurations> configurations) {
8385
return new WebApplicationContextRunner(contextFactory, initializers,
8486
environmentProperties, systemProperties, classLoader, parent,
85-
configurations);
87+
beanRegistrations, configurations);
8688
}
8789

8890
/**

spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunnerTests.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import static org.assertj.core.api.Assertions.assertThat;
4040
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
4141
import static org.assertj.core.api.Assertions.assertThatIOException;
42-
import static org.assertj.core.api.Assertions.entry;
4342

4443
/**
4544
* Abstract tests for {@link AbstractApplicationContextRunner} implementations.
@@ -167,15 +166,6 @@ public void runWithConfigurationsAndUserBeanShouldRegisterUserBeanLast() {
167166
});
168167
}
169168

170-
@Test
171-
public void runWithUserBeanShouldHaveAccessToContext() {
172-
get().withUserConfiguration(FooConfig.class)
173-
.withBean(String.class, (context) -> "Result: " + context.getBean("foo"))
174-
.run((context) -> assertThat(context.getBeansOfType(String.class))
175-
.containsOnly(entry("foo", "foo"),
176-
entry("string", "Result: foo")));
177-
}
178-
179169
@Test
180170
public void runWithMultipleConfigurationsShouldRegisterAllConfigurations() {
181171
get().withUserConfiguration(FooConfig.class)

0 commit comments

Comments
 (0)