spring boot 排除自动配置类的方式和原理

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();
	}
}
相关推荐
Devin~Y5 分钟前
电商AIGC智能客服面试:JVM调优、Spring Cloud微服务、Redis缓存、Kafka消息、K8s观测与RAG落地
java·jvm·spring boot·redis·spring cloud·kafka·kubernetes
Ai马猴子7 分钟前
企业定制专属模型,gpt-5.4-cdx高效适配,DMXAPI 安全合规
java·gpt·安全
星晨羽12 分钟前
Java通过FTP协议实现文件上传下载
java·开发语言
逸Y 仙X12 分钟前
文章三十:Elasticsearch SQL实战案例
java·大数据·sql·elasticsearch·搜索引擎·全文检索
小则又沐风a13 分钟前
初步了解进程的概念
java·linux·服务器·前端
斌果^O^14 分钟前
普通 SpringBoot 单体项目改造成微服务(Nacos+Gateway + 内部服务免鉴权)
java·spring boot·spring
摩羯座-小齐14 分钟前
java excel级联下拉框
java·excel
砍材农夫14 分钟前
物联网 基于netty入门与线程模型探秘简述
java·物联网·struts
GentleDevin15 分钟前
IntelliJ Idea常用快捷键(Window和Mac对照表)
java·ide·intellij-idea
Paxon Zhang15 分钟前
JavaEE 初阶变强宝典 **线程安全问题,线程状态,锁synchronized**
java·java-ee