文章目录
1. 自动配置的实现
SpringBoot
的自动配置机制为快速开发提供了极大的帮助,众所周知,其采用的是约定优于配置的理念。简单说就是把常用的三方工具预先配置好,这样开发人员只要处理好 jar 包依赖,再少量配置核心参数就可以使用三方工具了。事实上,SpringBoot
实现所谓的约定主要依赖以下两个关键
- 开启自动配置的注解
@EnableAutoConfiguration
META-INF/spring.factories
及META-INF/spring-autoconfigure-metadata.properties
文件。这两个文件主要用于配置接口的元数据及其实现类名称,是一种 SPI 机制,在程序中读取这些配置之后就可以确定接口的实现类
以下为 SpringBoot
实现自动配置的源码流程图,其中关于 ConfigurationClassParser
解析配置类的触发路径读者可阅读前置博客 SpringBoot 注解 @Import 的原理-ConfigurationClassPostProcessor 源码解析。其实本文可以看作是对这篇博客的补充,因为本文所涉及的流程已经包含在这篇博客中,只不过没有具体分析而已
读者需注意,在使用 SpringBoot 框架时必须要有一个观念,那就是框架中的大部分注解其实都对应了一个
BeanPostProcessor
接口的实现,这个接口是 Spring 框架实现 AOP 最重要的手段
2. 源码分析
2.1 @EnableAutoConfiguration 引入的 AutoConfigurationImportSelector 选择器类的解析
-
在
SpringBoot
框架中,每个程序都有一个启动类,启动类由@SpringBootApplication
注解标注出来。我们都知道这个注解其实是由 3 个注解组成,其中一个就是@EnableAutoConfiguration
,以下即为其定义。这个注解其实没有太多内容,在本文中需要关注的就是其通过@Import
注解引入了配置选择器类AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ String[] excludeName() default {}; }
-
@Import
注解背后的实现机制是完成自动配置的关键,读者可先阅读SpringBoot 注解 @Import 的原理-ConfigurationClassPostProcessor 源码解析 一文,本文直接从ConfigurationClassParser#parse()
方法解析配置类的触发点开始分析。可以看到,该方法其实主要调用了ConfigurationClassParser
内部的其他重载方法public void parse(Set<BeanDefinitionHolder> configCandidates) { for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } this.deferredImportSelectorHandler.process(); } protected final void parse(@Nullable String className, String beanName) throws IOException { Assert.notNull(className, "No bean class name for configuration class bean definition"); MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className); processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER); } protected final void parse(Class<?> clazz, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER); } protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); }
-
以上不管哪个
ConfigurationClassParser#parse()
方法被调用,最后都会调用到ConfigurationClassParser#processConfigurationClass()
方法。这个方法内部会在 while 循环中不断调用ConfigurationClassParser#doProcessConfigurationClass()
方法进行解析配置类的操作protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass, filter); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
-
ConfigurationClassParser#doProcessConfigurationClass()
方法的实现流程比较清晰,主要是解析各个注解引入的配置类。本文主要关注解析@Import
注解的操作,其处理主要分为了两个步骤:ConfigurationClassParser#getImports()
方法去获取当前处理的类上的@Import
引入的类ConfigurationClassParser#processImports()
方法处理@Import
引入的类
@Nullable protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass, filter); } // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), filter, true); // Process any @ImportResource annotations AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces processInterfaces(configClass, sourceClass); // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }
-
ConfigurationClassParser#getImports()
方法的逻辑很轻量,就是递归获取目标类上所有@Import
注解,并将该注解引入的类收集起来,本节步骤1 引入的AutoConfigurationImportSelector
在这个步骤就会被检测出来private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { Set<SourceClass> imports = new LinkedHashSet<>(); Set<SourceClass> visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited); return imports; } private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException { if (visited.add(sourceClass)) { for (SourceClass annotation : sourceClass.getAnnotations()) { String annName = annotation.getMetadata().getClassName(); if (!annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited); } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } }
-
ConfigurationClassParser#processImports()
方法的处理此处不做详细分析,简单来说这个步骤会分别解析@Import
注解引入的 3 种不同的类由于
AutoConfigurationImportSelector
实现了DeferredImportSelector
接口需要延后处理,则在此处其实不会直接回调其AutoConfigurationImportSelector#selectImports()
方法,而是调用this.deferredImportSelectorHandler.handle()
将其缓存起来private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
-
DeferredImportSelectorHandler#handle()
的源码如下,主要操作的流程如下:- 假如其内部
this.deferredImportSelectors
属性为 null,则直接将DeferredImportSelector
实例注册进DeferredImportSelectorGroupingHandler
实例,处理导入的类 - 如果内部
this.deferredImportSelectors
属性不为 null,则将其添加进缓存列表即可,通常是进入该分支处理
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector); if (this.deferredImportSelectors == null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); handler.register(holder); handler.processGroupImports(); } else { this.deferredImportSelectors.add(holder); } }
- 假如其内部
-
对于实现了
DeferredImportSelector
接口的选择器类,最终触发其相关方法需要回到本节步骤2ConfigurationClassParser#parse()
方法最后一行,即this.deferredImportSelectorHandler.process()
调用DeferredImportSelectorHandler#process()
方法。这个方法的处理步骤如下:- 首先新建
DeferredImportSelectorGroupingHandler
对象作为处理器 - 调用
DeferredImportSelectorGroupingHandler#register()
将缓存的DeferredImportSelector
选择器类依次注册到处理器中 - 调用处理器方法
DeferredImportSelectorGroupingHandler#processGroupImports()
完成选择器类中配置的类的解析
public void process() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { if (deferredImports != null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); deferredImports.forEach(handler::register); handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } }
- 首先新建
-
DeferredImportSelectorGroupingHandler#register()
方法的处理如下所示- 首先
deferredImport.getImportSelector().getImportGroup()
将调用DeferredImportSelector#getImportGroup()
方法获取导入组,对AutoConfigurationImportSelector
来说此方法返回一个内部类AutoConfigurationGroup
DeferredImportSelectorGroupingHandler#createGroup()
方法创建一个AutoConfigurationGroup
对象,并将其包装为DeferredImportSelectorGrouping
public void register(DeferredImportSelectorHolder deferredImport) { Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup(); DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); }
- 首先
-
DeferredImportSelectorGroupingHandler#processGroupImports()
方法的逻辑清晰易懂- 通过上一步注册的包装类
DeferredImportSelectorGrouping
对象调用DeferredImportSelectorGrouping#getImports()
方法去获取选择器配置的类。对于自动配置而言,这一步其实最后就是调用到AutoConfigurationGroup#process()
方法解析自动配置文件,之后调用AutoConfigurationGroup#selectImports()
返回解析结果, 下节将详细分析此关键步骤 - 遍历上一步解析获得的配置类集合,继续调用
ConfigurationClassParser#processImports()
方法,将解析获取的导入的类名entry.getImportClassName()
包装为候选类入参,完成导入的配置类的解析
public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { Predicate<String> exclusionFilter = grouping.getCandidateFilter(); grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } } public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this.group.selectImports(); }
- 通过上一步注册的包装类
2.2 spring.factories 自动配置文件的解析处理
-
AutoConfigurationGroup#process()
方法是将自动配置文件中的配置类
加载进 Spring 容器的关键,其处理主要分为以下几步:AutoConfigurationGroup#getAutoConfigurationMetadata()
方法加载自动配置类的元数据文件META-INF/spring-autoconfigure-metadata.properties
AutoConfigurationImportSelector#getAutoConfigurationEntry()
根据元数据配置去加载过滤META-INF/spring.factories
文件中的自动配置类- 将解析到的自动配置类信息添加到缓存,当
AutoConfigurationGroup#selectImports()
方法被调用时返回
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(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }
-
AutoConfigurationGroup#getAutoConfigurationMetadata()
方法其实就是个包装,最后会调用到AutoConfigurationMetadataLoader.loadMetadata()
方法加载自动配置类元数据private AutoConfigurationMetadata getAutoConfigurationMetadata() { if (this.autoConfigurationMetadata == null) { this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); } return this.autoConfigurationMetadata; }
-
AutoConfigurationMetadataLoader.loadMetadata()
方法的实现如下,可以看到其逻辑主要是从类加载器根目录下加载所有的META-INF/spring-autoconfigure-metadata.properties
文件,并将其中配置的属性包装为自动配置类元数据对象protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties"; static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); } 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); }
-
以下节选 SpringBoot 自动配置包
META-INF/spring-autoconfigure-metadata.properties
文件中响应式服务器工厂自动配置类配置 ReactiveWebServerFactoryAutoConfiguration 来解释其含义// 该配置等于号后面为空,表示没有为这个键配置对应的值 org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration= // 该配置表示这个 ReactiveWebServerFactoryAutoConfiguration 配置类可应用的前提是 web 应用的类型为 REACTIVE org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=REACTIVE // 该配置表示 ReactiveWebServerFactoryAutoConfiguration 配置类可应用的前提是必须存在类 ReactiveHttpInputMessage org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration.ConditionalOnClass=org.springframework.http.ReactiveHttpInputMessage
-
AutoConfigurationImportSelector#getAutoConfigurationEntry()
的处理步骤如下:AutoConfigurationImportSelector#getCandidateConfigurations()
方法去加载自动配置文件中配置的类名AutoConfigurationImportSelector#getExclusions()
获取代码中exclude
排除掉的类名,将其从缓存集合中去掉AutoConfigurationImportSelector#filter()
根据上一步获取的配置类元数据检查配置类的前置条件(例如ConditionalOnClass
)是否满足,不满足的也去除掉
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, 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 = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
-
AutoConfigurationImportSelector#getCandidateConfigurations()
方法其实也是个包装方法,最后会调用到SpringFactoriesLoader.loadFactoryNames()
方法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()
方法实现如下,其逻辑主要是从类加载器根目录下加载所有的META-INF/spring.factories
文件,并将其中配置的属性解析为接口名及其实现类名,以键值对形式存储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); } }
-
以下为节选 SpringBoot 自动配置包
META-INF/spring.factories
文件部分内容,可以看到以org.springframework.boot.autoconfigure.EnableAutoConfiguration
作为键的属性配置了很多实现类,这些实现类以逗号隔开,解析完成会进行相关条件的过滤筛选。经过本节所分析的处理,这些自动配置类的类名会暴露给 Spring 容器,后续的处理过程回到上一节步骤10完成配置类的解析# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ...... org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\ ......
2.3 自动配置原理的应用
经过以上分析,相信读者对 SpringBoot 的自动配置原理已经有了一定的了解,那么应该会比较容易想到第三方工具集成到 SpringBoot 的实现方式:
- 基于
@Import
注解
第三方工具包中提供一个注解引入其内部的配置类,例如 apollo 框架的@EnableApolloConfig
,在该注解上使用了@Import
引入自动配置类- 基于
@EnableAutoConfiguration
注解
第三方工具包在根目录下添加一个META-INF/spring.factories
文件,仿照 SpringBoot 的spring.factories
文件进行相关配置即可