“Spring Boot 自动包规则原理”的版本间的差异
| Jihongchang(讨论 | 贡献) | Jihongchang(讨论 | 贡献)  | ||
| 第124行: | 第124行: | ||
| …… | …… | ||
| </syntaxhighlight>SpringFactoriesLoader.java<syntaxhighlight lang="java"> | </syntaxhighlight>SpringFactoriesLoader.java<syntaxhighlight lang="java"> | ||
| − | …… | + | public final class SpringFactoriesLoader { | 
| + | 	…… | ||
| + | 	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; | ||
| + | 	…… | ||
| 	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { | 	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { | ||
| 		String factoryTypeName = factoryType.getName(); | 		String factoryTypeName = factoryType.getName(); | ||
| 		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); | 		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); | ||
| 	} | 	} | ||
| − | …… | + | 	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { | 
| − | </syntaxhighlight> | + | 		MultiValueMap<String, String> result = cache.get(classLoader); | 
| + | 		if (result != null) { | ||
| + | 			return result; | ||
| + | 		} | ||
| + | |||
| + | 		try { | ||
| + | 			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 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); | ||
| + | 		} | ||
| + | 	} | ||
| + | 	…… | ||
| + | } | ||
| + | </syntaxhighlight>利用工厂加载 <code>Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)</code> 得到所有的组件 | ||
2023年3月1日 (三) 09:59的版本
https://www.bilibili.com/video/BV19K4y1L7MT/?p=13
引导加载的自动配置类
@SpringBootApplication
……
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@SpringBootConfiguration
@Configuration
public @interface SpringBootConfiguration {
@Configuration
代表当前是一个配置类,使用 @SpringBootApplication 的类也会是 Spring Boot 中的一个配置类
@ComponentScan
包扫描注解,指定扫描哪些包;
@EnableAutoConfiguration
……
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
……
@AutoConfigurationPackage
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件
public @interface AutoConfigurationPackage {
比较关键的是 @Import(AutoConfigurationPackages.Registrar.class) 中导入的 AutoConfigurationPackages.Registrar
AutoConfigurationPackages.java
……	
	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}
		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImports(metadata));
		}
	}
……
利用 Registrar 向容器中导入一系列组件,AnnotationMetadata metadata 传入的是 @SpringBootApplication 注解标注类的注解元信息,
然后经由 new PackageImport(metadata).getPackageName() 得到 @SpringBootApplication 注解标注类的包名(比如:io.github.jihch),
最终目的是将指定一个包下(@SpringBootApplication 注解标注类的包)的所有组件导入进来,
这就解释了为什么默认是导入 @SpringBootApplication 注解标注类的包下的所有组件
https://www.bilibili.com/video/BV19K4y1L7MT?p=14
@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector.java
……	
        @Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
……
利用 getAutoConfigurationEntry(annotationMetadata) 往容器中批量导入一些组件:
……	
    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);
	}
……
用 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); 获取到所有需要导入到容器中的组件:
……
	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;
	}
……
SpringFactoriesLoader.java
public final class SpringFactoriesLoader {
	……
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
	……
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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 {
			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 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);
		}
	}
	……
}
利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) 得到所有的组件
