Allow multiple ModuleFilter implementations

This commit is contained in:
Niklas Herder
2023-07-22 12:22:02 +02:00
committed by Dave Syer
parent b202e964a2
commit 1dda492d57
4 changed files with 87 additions and 20 deletions

View File

@@ -114,9 +114,31 @@ was be bound to the autowired `spam()` method when Spring started.
### Filtering out modules from startup of ApplicationContext
In certain cases you might need to ensure that some modules are not configured at all, even though they might not be present in the final `ApplicationContext`.
This might be due to external code that may be hard to change that cause side effects at binding time, or for other reasons.
To ensure this you can define a `ModuleFilter` bean that will be applied for filtering the list of modules in the Guice context before they are touched by the Spring-Guice bridge. This will ensure that no `configure()` methods are called on the filtered modules.
In certain cases you might need to ensure that some modules are not configured at all, even though they will be absent from the final `ApplicationContext`.
This might be due to external code that may be hard to change that causes side effects at binding time, for example.
To ensure that these modules are not called at all you can define one or more`ModuleFilter` beans that will be applied to filter out modules from the context before they are touched by the Spring-Guice bridge.
```java
import org.springframework.context.annotation.Bean;
import org.springframework.guice.annotation.ModuleFilter;
public static class TestConfig {
@Bean
public static MyModule myModule() {
return new MyModule();
}
@Bean
public ModuleFilter myFilter() {
return module -> !(module instanceof MyModule);
}
}
```
This will ensure that no `configure()` methods are called on the filtered modules.
## Configuration Class Enhancements

View File

@@ -1,10 +1,37 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.guice.annotation;
import java.util.function.Predicate;
import com.google.inject.Module;
@FunctionalInterface
public interface ModuleFilter {
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
boolean filter(Module module);
/**
* A filter to remove {@link Module}:s from the context before any initialization code is
* run. If one implementation of this interface returns false from its
* {@link #test(Object)} method, the {@link Module} is removed. See
* {@link ModuleRegistryConfiguration#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)}
*
* @author Niklas Herder
*
*/
public interface ModuleFilter extends Predicate<Module> {
}

View File

@@ -125,18 +125,8 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Map<String, ModuleFilter> moduleFilters = ((ConfigurableListableBeanFactory) registry)
.getBeansOfType(ModuleFilter.class);
if (moduleFilters.size() > 1) {
throw new IllegalStateException(
"You can only define zero or one ModuleFilter implementation. Implementations found: "
+ String.join(",", moduleFilters.keySet()));
}
ModuleFilter moduleFilter = moduleFilters.values().stream().findFirst().orElse(module -> true);
List<Module> modules = new ArrayList<>(
((ConfigurableListableBeanFactory) registry).getBeansOfType(Module.class).values()).stream()
.filter(moduleFilter::filter).collect(Collectors.toList());
List<Module> modules = filterModules(registry,
new ArrayList<>(((ConfigurableListableBeanFactory) registry).getBeansOfType(Module.class).values()));
SpringModule module = new SpringModule((ConfigurableListableBeanFactory) registry,
this.enableJustInTimeBinding);
modules.add(module);
@@ -178,6 +168,16 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor
registry.registerBeanDefinition("guiceInjectorInitializer", beanDefinition);
}
private List<Module> filterModules(BeanDefinitionRegistry registry, List<Module> modules) {
Map<String, ModuleFilter> moduleFilters = ((ConfigurableListableBeanFactory) registry)
.getBeansOfType(ModuleFilter.class);
Predicate<Module> moduleFilter = (m) -> true;
for (Predicate<Module> value : moduleFilters.values()) {
moduleFilter = moduleFilter.and(value);
}
return modules.stream().filter(moduleFilter).collect(Collectors.toList());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) {

View File

@@ -83,6 +83,14 @@ public class EnableGuiceModulesTests {
context.close();
}
@Test
public void moduleBeanFiltersOutModulesWithMultipleFilters() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
FilteringModuleBeanConfig.class, PermissiveModuleFilterConfig.class);
assertThat(context.getBean(Foo.class)).isNotNull();
context.close();
}
@Test
public void testInjectorCreationDoesNotCauseCircularDependencyError() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
@@ -179,8 +187,18 @@ public class EnableGuiceModulesTests {
}
@Bean
ModuleFilter moduleFilter() {
return module -> !(module instanceof MyModule2);
static ModuleFilter moduleFilter() {
return (module) -> !(module instanceof MyModule2);
}
}
@Configuration
static class PermissiveModuleFilterConfig {
@Bean
static ModuleFilter moduleFilter2() {
return (module) -> true;
}
}