SpringBoot自动配置加载
SpringBoot自动配置加载
在使用SpringBoot时,我们只要导入好相关的jar,写好启动类,启动类上加入@SpringBootApplication注解,一个简单的SpringBoot项目就能够启动了。
这里记录一下SpringBoot是如何加载配置的。
-
@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) })其中
- @SpringBootConfiguration 表示当前类是一个配置类
- @ComponentScan标明了扫描路径
- @EnableAutoConfiguration 开启自动配置
-
@EnableAutoConfiguration
这个注解包含以下注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)其中
-
@AutoConfigurationPackage
这个注解中
@Import(AutoConfigurationPackages.Registrar.class)在AutoConfigurationPackages.Registrar类中,实现了按照包去加载所有的bean。
/** * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing * configuration. */ 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)); } }通过debug,可以看到,packageNames是当前启动器所在的包,所以使用了@EnableAutoConfiguration后,就能够加载自己定义的类中所有的bean对象。
-
@Import(AutoConfigurationImportSelector.class)
导入的AutoConfigurationImportSelector类中,可以找到以下方法
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }在这个方法中,去进行其它的jar包中的bean导入。
进入getAutoConfigurationEntry中
/** * 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); }List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);这段代码获得了所有的自动配置configuration。
/** * Return the auto-configuration class names that should be considered. By default * this method will load candidates using {@link SpringFactoriesLoader} with * {@link #getSpringFactoriesLoaderFactoryClass()}. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return a list of candidate configurations */ 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.loadFactoryNames得到的。进入方法,能够看到,它最终是这样去加载配置的。
/** * Load the fully qualified class names of factory implementations of the * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given * class loader. * <p>As of Spring Framework 5.3, if a particular implementation class name * is discovered more than once for the given factory type, duplicates will * be ignored. * @param factoryType the interface or abstract class representing the factory * @param classLoader the ClassLoader to use for loading resources; can be * {@code null} to use the default * @throws IllegalArgumentException if an error occurs while loading factory names * @see #loadFactories */ 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()); } 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 { //+++++这里!!! //FACTORIES_RESOURCE_LOCATION这个在类中定义为"META-INF/spring.factories" 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; }最终是通过
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);来加载所有的配置。即,在jar包中,获取META-INF/spring.factories中的配置。
我们可以在spring-boot-autoconfigure-2.4.5.jar中的META-INF/spring.factories里看到所有的auto configuration。
到那时并非这个配置中的所有configuration都会被加载。
在这个包中,能够看到有很多的加载类:
AopAutoConfiguration.java,
ApplicationAvailabilityAutoConfiguration.java,
BasicBatchConfigurer.java
......
进去会发现,它们都使用了@ConditionalOnBean(xxx.class),@ConditionalOnMissingBean(xxx.class)之类的条件注解,来确保运行正常。
所以,最终在加载bean的过程中,会根据用户引入的jar包,注入所使用的所有bean。
-

浙公网安备 33010602011771号