SpringBoot Starter加载原理分析

文章目录

  • 一、SpringBootApplication
      • 1、SpringBootApplication
  • 二、EnableAutoConfiguration
      • 1、EnableAutoConfiguration
  • 三、AutoConfigurationImportSelector
      • 1、process
      • 2、 getAutoConfigurationMetadata
      • 3、AutoConfigurationMetadataLoader.loadMetadata
      • 4、getAutoConfigurationEntry
      • 5、getCandidateConfigurations
      • 6、SpringFactoriesLoader.loadFactoryNames
      • 7、selectImports
  • 总结

办公设备租赁,深圳惠源.


一、SpringBootApplication

引入EnableAutoConfiguration注解

1、SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}

二、EnableAutoConfiguration

1、EnableAutoConfiguration

由@Import引入AutoConfigurationImportSelector组件

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

三、AutoConfigurationImportSelector

1、process

处理需要装配组件的数据

public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
// 获取所需要装配组件的数据
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

2、 getAutoConfigurationMetadata

public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
// 扫描并加载所有jar 下的META-INF/spring-autoconfigure-metadata.properties
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
			if (this.autoConfigurationMetadata == null) {
				this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
			}
			return this.autoConfigurationMetadata;
		}

3、AutoConfigurationMetadataLoader.loadMetadata

扫描并加载所有jar 下的META-INF/spring-autoconfigure-metadata.properties

protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
		try {
		    // 获取META-INF/spring-autoconfigure-metadata.properties的资源
			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);
		}
	}

4、getAutoConfigurationEntry

获取需要自动加载的组件

if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		// 获取EnableConfiguration注解的属性exclude
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 获取需要自动加载的组件
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		// 利用Set去除重复的组件
		configurations = removeDuplicates(configurations);
		// 获取排除属性 exclude, excludeName的值
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		// 根据exclude, excludeName排除Configuration
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		// 根据自定义的AutoConfigurationImportFilter排除Configuration
		configurations = filter(configurations, autoConfigurationMetadata);
		// 广播事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);

5、getCandidateConfigurations

获取需要自动加载的组件

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

6、SpringFactoriesLoader.loadFactoryNames

加载META-INF/spring-autoconfigure-metadata.properties的资源,并提取org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的值


public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 获取EnableAutoConfiguration全名称
		String factoryTypeName = factoryType.getName();
		// 从spring-autoconfigure-metadata.properties获取key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}


// 加载META-INF/spring-autoconfigure-metadata.properties的资源
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		// 从缓存中获取
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
		// 获取所有META-INF/spring-autoconfigure-metadata.properties的url
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			// 加载所有META-INF/spring-autoconfigure-metadata.properties资源并转为Map
			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();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			// 放入缓存
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

SpringBoot Starter加载原理分析_第1张图片

7、selectImports

将process步骤返回的conriguration给到Spring进行管理

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

总结

如果需要自己实现一个Starter
那只需要META-INF/spring-autoconfigure-metadata.properties的资源,并org.springframework.boot.autoconfigure.EnableAutoConfiguration的值为自己写的Configuration即可,一般框架会区分start包和真正实现的jar,然后由start包依赖真正实现的jar包

办公设备租赁,深圳惠源.

你可能感兴趣的:(Spring,spring,boot,java,后端)