24
24
25
25
import org .slf4j .Logger ;
26
26
import org .slf4j .LoggerFactory ;
27
+ import org .springframework .beans .BeansException ;
28
+ import org .springframework .beans .factory .config .ConfigurableListableBeanFactory ;
29
+ import org .springframework .beans .factory .support .BeanDefinitionRegistry ;
30
+ import org .springframework .beans .factory .support .BeanDefinitionRegistryPostProcessor ;
27
31
import org .springframework .context .ConfigurableApplicationContext ;
28
32
import org .springframework .modulith .core .ApplicationModule ;
33
+ import org .springframework .modulith .core .JavaPackage ;
29
34
import org .springframework .test .context .ContextConfigurationAttributes ;
30
35
import org .springframework .test .context .ContextCustomizer ;
31
36
import org .springframework .test .context .ContextCustomizerFactory ;
32
37
import org .springframework .test .context .MergedContextConfiguration ;
33
38
import org .springframework .test .context .TestContextAnnotationUtils ;
39
+ import org .springframework .util .Assert ;
34
40
35
41
/**
36
42
* @author Oliver Drotbohm
@@ -53,7 +59,6 @@ public ContextCustomizer createContextCustomizer(Class<?> testClass,
53
59
static class ModuleContextCustomizer implements ContextCustomizer {
54
60
55
61
private static final Logger LOGGER = LoggerFactory .getLogger (ModuleContextCustomizer .class );
56
- private static final String BEAN_NAME = ModuleTestExecution .class .getName ();
57
62
58
63
private final Supplier <ModuleTestExecution > execution ;
59
64
@@ -73,7 +78,9 @@ public void customizeContext(ConfigurableApplicationContext context, MergedConte
73
78
logModules (testExecution );
74
79
75
80
var beanFactory = context .getBeanFactory ();
76
- beanFactory .registerSingleton (BEAN_NAME , testExecution );
81
+ beanFactory .registerSingleton (ModuleTestExecution .class .getName (), testExecution );
82
+ beanFactory .registerSingleton (ModuleTestExecutionBeanDefinitionSelector .class .getName (),
83
+ new ModuleTestExecutionBeanDefinitionSelector (testExecution ));
77
84
78
85
var events = new DefaultPublishedEvents ();
79
86
beanFactory .registerSingleton (events .getClass ().getName (), events );
@@ -173,4 +180,78 @@ public int hashCode() {
173
180
return Objects .hash (execution );
174
181
}
175
182
}
183
+
184
+ /**
185
+ * A {@link BeanDefinitionRegistryPostProcessor} that selects
186
+ * {@link org.springframework.beans.factory.config.BeanDefinition}s that are either non-module beans (i.e.
187
+ * infrastructure) or beans living inside an {@link ApplicationModule} being part of the current
188
+ * {@link ModuleTestExecution}.
189
+ *
190
+ * @author Oliver Drotbohm
191
+ * @since 1.1
192
+ */
193
+ private static class ModuleTestExecutionBeanDefinitionSelector implements BeanDefinitionRegistryPostProcessor {
194
+
195
+ private static final Logger LOGGER = LoggerFactory .getLogger (ModuleTestExecutionBeanDefinitionSelector .class );
196
+
197
+ private final ModuleTestExecution execution ;
198
+
199
+ /**
200
+ * Creates a new {@link ModuleTestExecutionBeanDefinitionSelector} for the given {@link ModuleTestExecution}.
201
+ *
202
+ * @param execution must not be {@literal null}.
203
+ */
204
+ private ModuleTestExecutionBeanDefinitionSelector (ModuleTestExecution execution ) {
205
+
206
+ Assert .notNull (execution , "ModuleTestExecution must not be null!" );
207
+
208
+ this .execution = execution ;
209
+ }
210
+
211
+ /*
212
+ * (non-Javadoc)
213
+ * @see org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(org.springframework.beans.factory.support.BeanDefinitionRegistry)
214
+ */
215
+ @ Override
216
+ public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry ) throws BeansException {
217
+
218
+ if (!(registry instanceof ConfigurableListableBeanFactory factory )) {
219
+ return ;
220
+ }
221
+
222
+ var modules = execution .getModules ();
223
+
224
+ for (String name : registry .getBeanDefinitionNames ()) {
225
+
226
+ var type = factory .getType (name , false );
227
+ var module = modules .getModuleByType (type );
228
+
229
+ // Not a module type -> pass
230
+ if (module .isEmpty ()) {
231
+ continue ;
232
+ }
233
+
234
+ var packagesIncludedInTestRun = execution .getBasePackages ().toList ();
235
+
236
+ // A type of a module bootstrapped -> pass
237
+ if (module .map (ApplicationModule ::getBasePackage )
238
+ .map (JavaPackage ::getName )
239
+ .filter (packagesIncludedInTestRun ::contains ).isPresent ()) {
240
+ continue ;
241
+ }
242
+
243
+ LOGGER .trace ("Dropping bean definition {} for type {} as it is not included in an application module to be bootstrapped!" , name , type .getName ());
244
+
245
+ // Remove bean definition from bootstrap
246
+ registry .removeBeanDefinition (name );
247
+ }
248
+ }
249
+
250
+ /*
251
+ * (non-Javadoc)
252
+ * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
253
+ */
254
+ @ Override
255
+ public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory ) throws BeansException {}
256
+ }
176
257
}
0 commit comments