1919import java .util .ArrayList ;
2020import java .util .Collections ;
2121import java .util .List ;
22+ import java .util .function .Consumer ;
2223import java .util .function .Function ;
2324import java .util .function .Supplier ;
2425
26+ import org .springframework .beans .factory .config .BeanDefinition ;
27+ import org .springframework .beans .factory .config .BeanDefinitionCustomizer ;
2528import org .springframework .beans .factory .support .BeanNameGenerator ;
2629import org .springframework .boot .context .annotation .Configurations ;
2730import 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}
0 commit comments