背景

Spring Boot Starters 提供了很多常用的组件,有些组件例如 KafkaRocketMQ,引入到工程后会自动装配,它会检查您的配置,跟外部的组件创建一些连接,这个时候,我们很难控制 Kafka 关闭,RocketMQ 开启。

目标

给 Spring Boot Starters 的组件提供一个开关,控制自动装配。

实现

Spring Boot AutoCofiguration 提供了 AutoConfigurationImportFilter 过滤器,用来控制 @Configuration 配置类是否生效。查看 AutoConfigurationImportSelector 的源码,它会去调用 AutoConfigurationImportFilter 过滤器,传入所有的 @Configuration 配置类,通过 filter 方法筛选掉不需要的配置类,返回最新的配置类集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

private static class ConfigurationClassFilter {

private final AutoConfigurationMetadata autoConfigurationMetadata;

private final List<AutoConfigurationImportFilter> filters;

ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
this.filters = filters;
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//...
configurations = getConfigurationClassFilter().filter(configurations); // 过滤配置类
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
candidates[i] = null;
skipped = true; // 为 true 表示自动装配失效
}
}
}
if (!skipped) {
return configurations; // 返回对象表示自动装配生效
}
// ...
}
}
}

以 Kafka 为例,我们可以在 src/main/resources/META-INF 添加 spring.factories 文件,内容如下:

1
2
3
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.ylzl.spring.boot.kafka.autoconfigure.KafkaAutoConfigurationImportFilter

对应的代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class KafkaAutoConfigurationImportFilter implements AutoConfigurationImportFilter, EnvironmentAware {

private static final String MATCH_KEY = "spring.kafka.enabled";

private static final String[] IGNORE_CLASSES = {
"org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration",
"org.springframework.boot.actuate.autoconfigure.metrics.KafkaMetricsAutoConfiguration"
};

private Environment environment;

@Override
public void setEnvironment(@NotNull Environment environment) {
this.environment = environment;
}

@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
boolean disabled = !Boolean.parseBoolean(environment.getProperty(MATCH_KEY, Conditions.TRUE));
boolean[] match = new boolean[autoConfigurationClasses.length];
for (int i = 0; i < autoConfigurationClasses.length; i++) {
int index = i;
match[i] = !disabled || Arrays.stream(IGNORE_CLASSES).noneMatch(e -> e.equals(autoConfigurationClasses[index]));
}
return match; // 如果 match 为 false,对应的配置类不生效
}
}

代码扩展完成,当设置 spring.kafka.enabled=true 时开启 Kafka,设置 spring.kafka.enabled=false 时关闭 Kafka。

产出

  1. 支持工程内同时存在多种竞品组件,允许您按需开启和关闭。
  2. 解决第三方组件自动装配的问题,例如第三方项目,一个依赖引入多项配置,这些配置我们用不到,又修改不了相关源码,需要从扩展点屏蔽。