spring boot 排除自动配置类的方式和原理
自动配置类可以被以下三种方式排除:
通过 @SpringBootApplication
java
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@SpringBootApplication(excludeName = "org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration")
通过 @EnableAutoConfiguration
java
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
@EnableAutoConfiguration(excludeName = "org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration")
通过 spring.autoconfigure.exclude 属性配置
yaml
spring:
autoconfigure:
exclude:
- org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration
原理
自动配置是由 @SpringBootApplication 注解上的 @EnableAutoConfiguration 启用的。@EnableAutoConfiguration 通过 @Import(AutoConfigurationImportSelector.class) 导入了 AutoConfigurationImportSelector。
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
AutoConfigurationImportSelector 中的 getAutoConfigurationEntry 方法用于获取所有的自动配置类:
java
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 默认为 true
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取 @SpringBootApplication、@EnableAutoConfiguration 中的属性,即 exclude 和 excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// !!!这是重点,解析 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,获取所有的自动配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 使用 Set 去重
configurations = removeDuplicates(configurations);
// 获取应该排除的自动配置类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 删除应该排除的自动配置类
configurations.removeAll(exclusions);
// 根据 ConditionalOnBean、ConditionalOnClass 的 value 去加载类,若类不存在就可以提前过滤掉一部分
// 或者根据 ConditionalOnMissingBean、ConditionalOnMissingClass 的 value 去加载类,若类存在就可以提前过滤掉一部分
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
@Nullable AnnotationAttributes attributes) {
// 解析 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,获取所有的自动配置类
ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation,
getBeanClassLoader());
List<String> configurations = importCandidates.getCandidates();
Assert.state(!CollectionUtils.isEmpty(configurations),
"No auto configuration classes found in " + "META-INF/spring/"
+ this.autoConfigurationAnnotation.getName() + ".imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
ImportCandidates
java
public final class ImportCandidates implements Iterable<String> {
private static final String LOCATION = "META-INF/spring/%s.imports";
// 传入的 annotation 参数是 AutoConfiguration.class
public static ImportCandidates load(Class<?> annotation, @Nullable ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
ClassLoader classLoaderToUse = decideClassloader(classLoader);
// annotation.getName() 为 org.springframework.boot.autoconfigure.AutoConfiguration
// LOCATION = "META-INF/spring/%s.imports
// 格式化结果就是 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
String location = String.format(LOCATION, annotation.getName());
// 获取多个 imports 文件的路径
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> importCandidates = new ArrayList<>();
// 循环读取 imports 文件
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// readCandidateConfigurations 读取文件内容
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates);
}
private static List<String> readCandidateConfigurations(URL url) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new UrlResource(url).getInputStream(), StandardCharsets.UTF_8))) {
List<String> candidates = new ArrayList<>();
String line;
// 逐行读取
while ((line = reader.readLine()) != null) {
line = stripComment(line);
line = line.trim();
if (line.isEmpty()) {
continue;
}
// 一行一个配置类
candidates.add(line);
}
return candidates;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load configurations from location [" + url + "]", ex);
}
}
}
AutoConfigurationImportSelector#getExclusions
java
public class AutoConfigurationImportSelector {
// spring.autoconfigure.exclude 属性 key
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
protected Set<String> getExclusions(AnnotationMetadata metadata, @Nullable AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
if (attributes != null) {
// 添加 @SpringBootApplication、@EnableAutoConfiguration 配置的
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(asList(attributes, "excludeName"));
}
// 添加 spring.autoconfigure.exclude 属性配置的
excluded.addAll(getExcludeAutoConfigurationsProperty());
return getAutoConfigurationReplacements().replaceAll(excluded);
}
protected List<String> getExcludeAutoConfigurationsProperty() {
Environment environment = getEnvironment();
if (environment == null) {
return Collections.emptyList();
}
if (environment instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(environment);
// 从 Environment 中获取 spring.autoconfigure.exclude 的值
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class)
.map(Arrays::asList)
.orElse(Collections.emptyList());
}
// // 从 Environment 中获取 spring.autoconfigure.exclude 的值
String[] excludes = environment.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}
}