spring boot源码(二) autoconfigure

spring boot 通过@EnableAutoConfiguration开启autoconfigure功能。

点开注解,可以看到有一个@Import注解,AutoConfigurationImportSelector,实现了DeferredImportSelector接口,DeferredImportSelector和ImportSelector有所区别,DeferredImportSelector将会执行DeferredImportSelector.Group中的process方法,并且接着调用selectImports,先看process


	@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
					() -> String.format("Only %s implementations are supported, got %s",
							AutoConfigurationImportSelector.class.getSimpleName(),
							deferredImportSelector.getClass().getName()));
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

看getAutoConfigurationEntry方法,在getCandidateConfigurations中,从META-INF/spring.factories文件中获取所有键是org.springframework.boot.autoconfigure.EnableAutoConfiguration的类的集合configurations,接着移除重复的,再从annotation的元数据中获取到excluede,以及filter,执行过滤排除,进一步得到需要的类。

/**
	 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
	 * of the importing {@link Configuration @Configuration} class.
	 * @param annotationMetadata the annotation metadata of the configuration class
	 * @return the auto-configurations that should be imported
	 */
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

这里要详细看一看filter的过程,首先会获取一个filters的集合

	protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
		return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
	}

	private ConfigurationClassFilter getConfigurationClassFilter() {
		if (this.configurationClassFilter == null) {
			List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
			for (AutoConfigurationImportFilter filter : filters) {
				invokeAwareMethods(filter);
			}
			this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
		}
		return this.configurationClassFilter;
	}

 这里可以看到,从META-INF/spring.factories文件中获取AutoConfigurationImportFilter的实现类,当前默认只有spring-boot-autoconfigure这个包下面的META-INF/spring.factories文件存在,而这个文件中有三个AutoConfigurationImportFilter的实现类,、分别是OnClassCondition,OnWebApplicationCondition以及OnBeanCondition。

接着创建了一个ConfigurationClassFilter,重点看一下这个构造方法

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

调用了AutoConfigurationMetadataLoader的loadMetadata方法,方法中会去扫描一个固定的路径META-INF/spring-autoconfigure-metadata.properties,将所有该路径下文件中的配置都加载到一个properties中,接着创建一个PropertiesAutoConfigurationMetadata返回

	static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
		try {
			Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
					: ClassLoader.getSystemResources(path);
			Properties properties = new Properties();
			while (urls.hasMoreElements()) {
				properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
			}
			return loadMetadata(properties);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
		}
	}

	static AutoConfigurationMetadata loadMetadata(Properties properties) {
		return new PropertiesAutoConfigurationMetadata(properties);
	}

接着调用ConfigurationClassFilter的filter方法,分析一下代码,首先循环调用三个filter类,参数分别是从META-INF/spring.factories中获取的configurations集合以及从META-INF/spring-autoconfigure-metadata.properties中获取的autoConfigurationMetadata集合,然后进行过滤,符合的留下,不符合的置为空,过滤完成之后,接着再次循环,将空的移除,返回留下来的。


		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;
					}
				}
			}
			if (!skipped) {
				return configurations;
			}
			List<String> result = new ArrayList<>(candidates.length);
			for (String candidate : candidates) {
				if (candidate != null) {
					result.add(candidate);
				}
			}
			if (logger.isTraceEnabled()) {
				int numberFiltered = configurations.size() - result.size();
				logger.trace("Filtered " + numberFiltered + " auto configuration class in "
						+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
			}
			return result;
		}

	}

所以要看看三个filter中究竟做了什么事情。

首先看OnClassCondition的调用

	public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
		ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
		ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
		boolean[] match = new boolean[outcomes.length];
		for (int i = 0; i < outcomes.length; i++) {
			match[i] = (outcomes[i] == null || outcomes[i].isMatch());
			if (!match[i] && outcomes[i] != null) {
				logOutcome(autoConfigurationClasses[i], outcomes[i]);
				if (report != null) {
					report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
				}
			}
		}
		return match;
	}

首先向spring容器中注册一个ConditionEvaluationReport并返回

/**
	 * Obtain a {@link ConditionEvaluationReport} for the specified bean factory.
	 * @param beanFactory the bean factory
	 * @return an existing or new {@link ConditionEvaluationReport}
	 */
	public static ConditionEvaluationReport get(ConfigurableListableBeanFactory beanFactory) {
		synchronized (beanFactory) {
			ConditionEvaluationReport report;
			if (beanFactory.containsSingleton(BEAN_NAME)) {
				report = beanFactory.getBean(BEAN_NAME, ConditionEvaluationReport.class);
			}
			else {
				report = new ConditionEvaluationReport();
				beanFactory.registerSingleton(BEAN_NAME, report);
			}
			locateParent(beanFactory.getParentBeanFactory(), report);
			return report;
		}
	}

接着调用getOutcomes方法,这里直接去看核心的代码,会创建一个StandardOutcomesResolver,调用getOutcomes方法,注意这里的autoConfigurationClasses就是之前的configurations集合,而autoConfigurationMetadata自然是之前的autoConfigurationMetadata集合,这里是循环configurations集合,从autoConfigurationMetadata这个properties的集合中获取当前configuration的名称加上.再加上ConditionalOnClass,假设当前configuration名称是com.aa.xx那么结合就是com.aa.xx.ConditionalOnClass这样一个properties键名,从而获取对应的值。

大致流程先说明一下,这里就是从META-INF/spring.factories中获取到的autoconfigure的类名A类,然后再从META-INF/spring-autoconfigure-metadata.properties中获取类名加上ConditionalOnClass的键的值,值就是另外一个类B类,就是说autoconfigure的类需要这个类存在才能加载,所以再接下来执行的getOutcome方法就是判断这另外的那个B类存不存在

	private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
				AutoConfigurationMetadata autoConfigurationMetadata) {
			ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
			for (int i = start; i < end; i++) {
				String autoConfigurationClass = autoConfigurationClasses[i];
				if (autoConfigurationClass != null) {
					String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
					if (candidates != null) {
						outcomes[i - start] = getOutcome(candidates);
					}
				}
			}
			return outcomes;
		}

再来看getOutcome方法,最后会调用getOutcome方法,首先调用matches方法,方法中就是看能不能用类加载器加载到这个类,如果可以加载返回null,否则就构建一个ConditionOutcome,这个ConditionOutcome表示了ConditionalOnClass这个条件下,没有找到必须的类。

	private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
			if (ClassNameFilter.MISSING.matches(className, classLoader)) {
				return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
						.didNotFind("required class").items(Style.QUOTE, className));
			}
			return null;
		}

回到match方法,往下,返回null的是true,否则是false,并且trace级别的日志打印,并且记录到了ConditionEvaluationReport里面

接着第二个filter,OnWebApplicationCondition,在了解了OnClassCondition的原理之后,后面的两个filter功能基本类似,在OnWebApplicationCondition中,从META-INF/spring-autoconfigure-metadata.properties中获取类名加上ConditionalOnWebApplication的值,如果指定是SERVLET,会判断是否能加载org.springframework.web.context.support.GenericWebApplicationContext,如果指定是REACTIVE,会判断能否加载org.springframework.web.reactive.HandlerResult,如果其他,就会判断以上其中一个是否能加载到。其他逻辑和第一个filter基本类似。同样返回一个true和false的集合,再到一个循环中,将false的置为null,因此这三个filter是&&的关系。

private ConditionOutcome getOutcome(String type) {
		if (type == null) {
			return null;
		}
		ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnWebApplication.class);
		if (ConditionalOnWebApplication.Type.SERVLET.name().equals(type)) {
			if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS, getBeanClassLoader())) {
				return ConditionOutcome.noMatch(message.didNotFind("servlet web application classes").atAll());
			}
		}
		if (ConditionalOnWebApplication.Type.REACTIVE.name().equals(type)) {
			if (!ClassNameFilter.isPresent(REACTIVE_WEB_APPLICATION_CLASS, getBeanClassLoader())) {
				return ConditionOutcome.noMatch(message.didNotFind("reactive web application classes").atAll());
			}
		}
		if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS, getBeanClassLoader())
				&& !ClassUtils.isPresent(REACTIVE_WEB_APPLICATION_CLASS, getBeanClassLoader())) {
			return ConditionOutcome.noMatch(message.didNotFind("reactive or servlet web application classes").atAll());
		}
		return null;
	}

再看第三个OnBeanCondition,这个filter和之前不同的地方是从META-INF/spring-autoconfigure-metadata.properties中获取类名加上ConditionalOnBean以及ConditionalOnSingleCandidate的值,判断能否加载到,过程和之前都类似。

	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) {
				Set<String> onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean");
				outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);
				if (outcomes[i] == null) {
					Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,
							"ConditionalOnSingleCandidate");
					outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);
				}
			}
		}
		return outcomes;
	}

最后经过三个filter之后, 筛选出符合条件的configurations

接着,从META-INF/spring.factories文件中获取所有的AutoConfigurationImportListener监听器,发布AutoConfigurationImportEvent事件,当前默认只有一个ConditionEvaluationReportAutoConfigurationImportListener,ConditionEvaluationReportAutoConfigurationImportListener的onAutoConfigurationImportEvent方法接收事件

取出ConditionEvaluationReport,保存了所有的configurations以及需要排除的excludes,返回。

接着在调用selectImports方法的时候进行排除,以及进行一个排序操作

public Iterable<Entry> selectImports() {
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			processedConfigurations.removeAll(allExclusions);

			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}

 最后返回的就是需要生成Bean的类型的迭代器。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值