SpringBoot中的spi机制(2): 实现原理


SpringBoot中的spi机制(1): 一个Demo了解什么是spi中,通过demo了解了spi机制的应用,本文将学习其原理。

1 @EnableAutoConfiguration

在springBoot的启动类的复合注解@SpringBootApplication,主要包括图中三个注解
在这里插入图片描述
这里只说@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};
	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};
}

@EnableAutoConfiguration 的 主 要 作 用 其 实 就 是 帮 助springboot 应用把所有符合条件的@Configuration 配置都加载到当前 SpringBoot 创建并使用的IOC容器中。

2 @Import(AutoConfigurationImportSelector.class)

@Import的作用就是把多个容器配置合并到一个配置中。里面的值可以是三大类:

  1. 普通bean或者带@Configuration的bean进行注入
  2. 实现ImportSlector接口进行动态注入
  3. 实现ImportBeanDefinitionRegistrar接口进行动态注入

AutoConfigurationImportSelector就是上面的第二类,接下来开始看源码。

3 AutoConfigurationImportSelector

简单概述下流程:
先扫描spring-autoconfigure-metadata.properties文件,再扫描spring.factories,然后用前者过滤后者数据,最后返回需要注入的类的全路径数组。

为什么要过滤呢? 原因是很多的@Configuration 其实是依托于其他的框架来加载的,如果当前的 classpath 环境下没有相关联的依赖,则意味着这些类没必要进行加载,所以,通过这种条件过滤可以
有效的减少@configuration 类的数量从而降低SpringBoot 的启动时间。
条件过滤

3.1 AutoConfigurationImportSelector#selectImports

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
		//返回需要加载的类路径数组
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

3.2 META-INF/spring-autoconfigure-metadata.properties

	protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";
	public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
		return loadMetadata(classLoader, PATH);
	}

可以看到加载META-INF/spring-autoconfigure-metadata.properties文件。

3.3 META-INF/spring.factories

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//获取注解里的值,比如exclue、exclueName
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//获取所有Spring.factories的类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		//去重
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		//根据spring-autoconfigure-metadata.properties里的条件过滤spring.factories里扫描到的类
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
	
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}
	
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		try {
			Enumeration<URL> urls = (classLoader != null ?
					//META-INF/spring.factories
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			//dosomething ......
			//..................
	}	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值