【SpringBoot】自动装配原理(二):AutoConfigurationImportSelector 的 selectImports

SpringBoot 专栏收录该内容
12 篇文章 1 订阅

AutoConfigurationImportSelector

该类实现ImportSelector接口,最重要的是实现selectImports方法,该方法的起到的作用是,根据配置文件(spring.factories),将需要注入到容器的bean注入到容器。

selectImports

	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());
	}

首先我们看下,怎样判断自动装配开关的:

protected boolean isEnabled(AnnotationMetadata metadata) {   
    // 判断当前实例的class   
    if (getClass() == AutoConfigurationImportSelector.class) {      
        // 返回 spring.boot.enableautoconfiguration 的值,如果为null,返回true      
        // spring.boot.enableautoconfiguration 可在配置文件中配置,不配则为null      
        return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);   
    }   
    return true; 
} 

接下来,我们看如何获取需要装配的bean:

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {   
    // 检查自动装配开关   
    if (!isEnabled(annotationMetadata)) {      
        return EMPTY_ENTRY;   
    }   
    // 获取EnableAutoConfiguration中的参数,exclude()/excludeName()   
    AnnotationAttributes attributes = getAttributes(annotationMetadata);   
    // 获取需要自动装配的所有配置类,读取META-INF/spring.factories   
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);   
    // 去重,List转Set再转List   
    configurations = removeDuplicates(configurations);   
    // 从EnableAutoConfiguration的exclude/excludeName属性中获取排除项   
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);   
    // 检查需要排除的类是否在configurations中,不在报错   
    checkExcludedClasses(configurations, exclusions);   
    // 从configurations去除exclusions   
    configurations.removeAll(exclusions);   
    // 对configurations进行过滤,剔除掉不满足 spring-autoconfigure-metadata.properties 所写条件的配置类   
    configurations = filter(configurations, autoConfigurationMetadata);  
    // 监听器 import 事件回调 
    fireAutoConfigurationImportEvents(configurations, exclusions);   
    // 返回(configurations, exclusions)组   
    return new AutoConfigurationEntry(configurations, exclusions); } 

可见selectImports()AutoConfigurationImportSelector核心方法

该方法的功能主要是以下三点:

  • 获取META-INF/spring.factoriesEnableAutoConfiguration所对应的Configuration类列表
  • @EnableAutoConfiguration注解中的exclude/excludeName参数筛选一遍
  • 再由私有内部类ConfigurationClassFilter筛选一遍,即不满足@Conditional的配置类

源码流程

AutoConfigurationMetadataLoader

AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader)

  • 读取所有 classPath 下的 spring-autoconfigure-metadata.properties

img
结果如下:
img

getAutoConfigurationEntry()

  • SprinBoot框架层帮忙做的自动装配元数据

img

getAttributes()

AnnotationAttributes attributes = getAttributes(annotationMetadata)

  • 获取@EnableAutoConfiguration标注类的元信息。
	protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
		String name = getAnnotationClass().getName();
		AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
		Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
				+ " annotated with " + ClassUtils.getShortName(name) + "?");
		return attributes;
	}

getCandidateConfigurations()

List configurations = getCandidateConfigurations(annotationMetadata, attributes)

  • 读取所有 classPath 下的 spring.factories
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		// 加载 spring.factories 文件
		// getSpringFactoriesLoaderFactoryClass() = EnableAutoConfiguration.class
		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;
	}

SpringFactoriesLoader.loadFactoryNames

	
	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

在这里插入图片描述

返回的是 :key = org.springframework.boot.autoconfigure.EnableAutoConfiguration,对应的values值

  • 这些values即是SpringBoot默认的自动装配类,所以有时候读者阅读源码时,发现某些类莫名其妙的被装载到Spring容器中了,一部分原因可能是这个地方搞的鬼。

removeDuplicates()

configurations = removeDuplicates(configurations)

  • 移除重复定义的配置类( 利用set集合的不可重复性 )
	protected final <T> List<T> removeDuplicates(List<T> list) {
		return new ArrayList<>(new LinkedHashSet<>(list));
	}

getExclusions()

Set exclusions = getExclusions(annotationMetadata, attributes)

  • 获取排除类名单,排除类可通过 exclude = {A.class.B.class}属性来排除指定的配置类。
	// attributes 就是第一步拿到的 AnnotationMetadata 的属性
	protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		Set<String> excluded = new LinkedHashSet<>();
		excluded.addAll(asList(attributes, "exclude"));
		excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
		excluded.addAll(getExcludeAutoConfigurationsProperty());
		return excluded;
	}

checkExcludedClasses

checkExcludedClasses(configurations, exclusions)

  • 检查被 ExcludedClasses 的类是否存在现在的 beanFacotry 中
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
		List<String> invalidExcludes = new ArrayList<>(exclusions.size());
		for (String exclusion : exclusions) {
			if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
				invalidExcludes.add(exclusion);
			}
		}
		if (!invalidExcludes.isEmpty()) {
			handleInvalidExcludes(invalidExcludes);
		}
	}

filter()

configurations = filter(configurations, autoConfigurationMetadata)

  • 对configurations进行过滤,剔除掉条件不成立的配置类

img

①:

  • 调用的是 SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader),也是在spring.factories中获取 AutoConfigurationImportFilter类型的过滤器,此处默认有

    img

②:

  • 分别执行配置类的match方法,由于 OnBeanCondition、OnClassCondition、OnWebApplicationCondition 均继承自 FilteringSpringBootCondition,match方法如下:

    在这里插入图片描述

  • 三个子类 img
    在这里插入图片描述

实现 getOutcomes

  • 通过上面三个子类的方法实现 ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata)

  • 此处拿OnBeanCondition类来分析:

    img

自动装配类集合迭代调用 autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean") 方法获取 配置类.ConditionalOnBean 的元信息

  • 即在元数据配置文件中的 values。
    img
  • 以 RedisCacheConfiguration为例,其 “conditionOnBean” 如下:
    img

获取返回的values值后,再调用 getOutcome()方法计算匹配结果

  •  protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
     		AutoConfigurationMetadata autoConfigurationMetadata) {
     	ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
     	for (int i = 0; i < outcomes.length; i++) {
     		String autoConfigurationClass = autoConfigurationClasses[i];
     		if (autoConfigurationClass != null) {
     			// 获得配置中 autoConfigurationClasses[i] 的 ConditionalOnBean
     			Set<String> onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean");
     			// 获得 ConditionalOnBean 没有匹配上的 Bean
     			outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);
     			// 如果 ConditionalOnBean  都匹配上了
     			if (outcomes[i] == null) {
     				Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,
     						"ConditionalOnSingleCandidate");
     				outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);
     			}
     		}
     	}
     	return outcomes;
     }
    
     private ConditionOutcome getOutcome(Set<String> requiredBeanTypes, Class<? extends Annotation> annotation) {
     	List<String> missing = filter(requiredBeanTypes, ClassNameFilter.MISSING, getBeanClassLoader());
     	if (!missing.isEmpty()) {
     		ConditionMessage message = ConditionMessage.forCondition(annotation)
     				.didNotFind("required type", "required types").items(Style.QUOTE, missing);
     		return ConditionOutcome.noMatch(message);
     	}
     	return null;
     }
    
  • 最终判断是由 ClassNameFilter.MISSING#matches 决定的。

fireAutoConfigurationImportEvents()

fireAutoConfigurationImportEvents(configurations, exclusions)

  • 监听器 import 事件回调
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
   	List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
   	if (!listeners.isEmpty()) {
   		AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
   		for (AutoConfigurationImportListener listener : listeners) {
   			invokeAwareMethods(listener);
   			listener.onAutoConfigurationImportEvent(event);
   		}
   	}
}
  • 3
    点赞
  • 2
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值