SpringBoot自动装配

目录

SpringBootApplication

自动装配原理

读取候选装配组件

排除自动装配组件

过滤自动装配组件

 自动装配事件

自定义Spring Boot自动装配

自动装配Class命名规则

自动装配package命名规则

自定义Spring Boot Starter

Spring Boot条件化自动装配

Class条件注解

Bean条件注解


SpringBootApplication

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication

@EnableAutoConfiguration用于激活Spring Boot自动装配的特性

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration 

自动装配原理

 组件自动装配逻辑均在selectImports(AnnotationMetadata)方法中实现。

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

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

 AutoConfigurationImportSelector读取自动装配Class的流程为:

1. 通过SpringFactoriesLoader#loadFactoryNames(Class,ClassLoader)方法读取所有META-INF/spring.factories资源中@EnableAutoConfiguration所关联的自动装配Class集合

2. 读取当前配置类所标注的@EnableAutoConfiguration属性exclude和excludeName,并与spring.autoconfigure.exclude配置属性合并为自动装配Class排除集合

3. 检查自动装配Class排除集合是否合法

4. 排除候选自动装配Class集合中的排除名单

5. 再次过滤候选自动装配Class集合中Class不存在的成员

6. 自动装配Class读取完毕后,fireAutoConfigurationImportEvents(List,Set)方法被执行

读取候选装配组件

getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	List<String> configurations = new ArrayList<>(
				SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
	ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
	Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
						+ "are using a custom packaging, make sure that file is correct.");
	return configurations;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	ClassLoader classLoaderToUse = classLoader;
	if (classLoaderToUse == null) {
		classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	}
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

 SpringFactoriesLoader是Spring Framework工厂机制的加载器,loadFactoryNames(Class, ClassLoader)方法加载原理如下:

1. 搜索指定ClassLoader下所有的META-INF/spring.factories资源内容(可能存在多个)

2. 将一个或多个META-INF/spring.factories资源内容作为Properties文件读取,合并为一个Key为接口的全类名,Value是实现类全类名列表的Map,作为loadSpringFactories(ClassLoader)方法的返回值

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

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}
protected final <T> List<T> removeDuplicates(List<T> list) {
   return new ArrayList<>(new LinkedHashSet<>(list));
}

排除自动装配组件

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	Set<String> excluded = new LinkedHashSet<>();
	excluded.addAll(asList(attributes, "exclude"));
	excluded.addAll(asList(attributes, "excludeName"));
	excluded.addAll(getExcludeAutoConfigurationsProperty());
	return excluded;
}
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

protected List<String> getExcludeAutoConfigurationsProperty() {
	Environment environment = getEnvironment();
	if (environment == null) {
		return Collections.emptyList();
	}
	if (environment instanceof ConfigurableEnvironment) {
		Binder binder = Binder.get(environment);
		return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
					.orElse(Collections.emptyList());
	}
	String[] excludes = environment.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
	return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}

        将标注@EnableAutoConfiguration配置类的注解属性(AnnotationAttributes)exclude和excludeName,以及将spring.autoconfigure.exclude配置值累加至排除集合excluded。

        

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);
	}
}
protected void handleInvalidExcludes(List<String> invalidExcludes) {
	StringBuilder message = new StringBuilder();
	for (String exclude : invalidExcludes) {
		message.append("\t- ").append(exclude).append(String.format("%n"));
	}
	throw new IllegalStateException(String.format(
				"The following classes could not be excluded because they are not auto-configuration classes:%n%s",
				message));
}

过滤自动装配组件

private static class ConfigurationClassFilter {
	private final AutoConfigurationMetadata autoConfigurationMetadata;
	private final List<AutoConfigurationImportFilter> filters;
	ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
		this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
		this.filters = filters;
	}
	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;
	}
}

 自动装配事件

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

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

自定义Spring Boot自动装配

        当注解@EnableAutoConfiguration激活自动装配后,META-INF/spring.factories资源中声明的配置Class随机被装配。这种机制不仅在Spring Boot框架内部生效,也允许开发人员自定义Spring Boot自动装配。

自动装配Class命名规则

*AutoConfiguration

自动装配package命名规则

SpringBoot内建自动装配Class,package基本模式为:

org.springframework.boot.autoconfigure

|- ${module-package}

        |- *AutoConfiguration

        |- ${sub-module-package}

                |- ...

按此规律,自动装配package命名模式应该是:

${root-package}

|- ${module-package}

        |- *AutoConfiguration

        |- ${sub-module-package}

                |- ...

自定义Spring Boot Starter

Spring Boot Starter基础依赖

<dependeny>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <optional>true</optional>
</dependeny>

实现自动装配:

@Configuration
public class *AutoConfiguration {

    ……
}

META-INF/spring.factories资源中声明*AutoConfiguration

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
**.*AutoConfiguraton

Spring Boot条件化自动装配

Class条件注解

@ConditionalOnClass和@ConditionalOnMissingClass

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

	/**
	 * The classes that must be present. Using this attribute is safe when using
	 * {@code ConditionalOnClass} at class level, but it must not be used when using
	 * {@code ConditionalOnClass} on a {@code @Bean} method.
	 * <p>
	 * Since this annotation is parsed by loading class bytecode, it is safe to specify
	 * classes here that may ultimately not be on the classpath, only if this annotation
	 * is directly on the affected component and <b>not</b> if this annotation is used as
	 * a composed, meta-annotation. In order to use this annotation as a meta-annotation,
	 * only use the {@link #name} attribute.
	 * @return the classes that must be present
	 */
	Class<?>[] value() default {};

	/**
	 * The class names that must be present.
	 * @return the class names that must be present.
	 */
	String[] name() default {};

}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnMissingClass {

	/**
	 * The names of the classes that must not be present.
	 * @return the names of the classes that must not be present
	 */
	String[] value() default {};

}
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {
    ……
    @Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		ClassLoader classLoader = context.getClassLoader();
		ConditionMessage matchMessage = ConditionMessage.empty();
		List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
		if (onClasses != null) {
			List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
			if (!missing.isEmpty()) {
				return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
						.didNotFind("required class", "required classes").items(Style.QUOTE, missing));
			}
			matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
					.found("required class", "required classes")
					.items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
		}
		List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
		if (onMissingClasses != null) {
			List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
			if (!present.isEmpty()) {
				return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class)
						.found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
			}
			matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
					.didNotFind("unwanted class", "unwanted classes")
					.items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
		}
		return ConditionOutcome.match(matchMessage);
	}
……
}

Bean条件注解

@ConditionalOnBean和@ConditionalOnMissingBean

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值