1.环境和依赖
1.1.spring boot版本
springboot 2.2.X版本采用的maven构建,2.3.X采用gradle构建,因此采用2.2.X,mavan构建的便于源码阅读。本文以2.2.9为例进行Spring Boot自动装配原理的解析。
1.2.依赖管理
引入Spring Boot的方式有两种
引入spring-boot-dependencies的pom文件
将spring-boot-starter-parent作为父级pom
这两种方式的底层都是都是一样的,都是引入了spring-boot-dependencies这个pom文件来管理Spring Boot的所有依赖。
SpringBoot中将一类场景要用到的依赖封装成一个starter,spring-boot-dependencies中包含了J2EE中所有场景(starter)的依赖,并声明了依赖的版本号。
2.自动装配
2.1.流程概述
首先所有JAVA程序的入口都是main方法,Spring Boot也不例外,只有main方法执行时,所有流程步骤才会执行,此处我们只是从启动流程中剥离出和自动装配相关的流程来进行单独解析。只需要大致知道自动装配流程有几步即可,如果有其它疑惑看后文的启动过程解析,就能豁然开朗。
自动装配的整个流程可以分为三大步
- 获取过滤列表
- 获取自动配置类列表
- 比对移除、封装返回
1.获取条件列表
获取类自动装载的条件列表。
2.获取自动配置列表
获取自动装载类的列表。
3.比对移除、封装返回
按照条件列表,将不满足被自动装载条件的类移除掉,返回满足条件的类列表。
2.2.三大步前的准备工作
2.2.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启动自动装配:
@Import(AutoConfigurationImportSelector.class) ,AutoConfigurationImportSelector会完成所有配置类的获取以及相关的准备工作。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}
2.2.2.获取所有配置类
AutoConfigurationImportSelector被加载后,经过层层调用,最终会调用到DeferredImportSelector中:
会去扫描所有@Configuration封装成一个列表返回。
public Iterable<Entry> getImports() { Iterator var1 = this.deferredImports.iterator(); while(var1.hasNext()) { ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var1.next(); this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } //将得到的自动配置类按照@order进行排序 return this.group.selectImports(); }
2.3.获取过滤列表
2.3.1.目的
获取过滤列表,即去获取META-INF/spring-autoconfigure-metadata.properties这一文件。这个文件中会详细记录Spring Boot自带的各大J2EE场景的自动配置类(@Configuration)各自被自动装载生效的前提条件是什么。
2.3.2.过程
DeferredImportSelector.Group.process()中会首先获取自动装配的过滤条件列表,该列表中记录了待装配的类的装配条件。获取的核心方法是getAutoConfigurationMetadata(),该方法会根据传过来的ClassLoader去遍历加载classpath下的所有依赖,获取依赖中的META-INF/spring-autoconfigure-metadata.properties文件。
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); } } private AutoConfigurationMetadata getAutoConfigurationMetadata() { if (this.autoConfigurationMetadata == null) { this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); } return this.autoConfigurationMetadata; }
final class AutoConfigurationMetadataLoader { protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties"; private AutoConfigurationMetadataLoader() { } 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); } }
过滤列表中会以KV键值对的方式记录装配条件,例如:
org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.annotation.EnableRabbit
2.4.装载
2.4.1.目的
- 获取自动配置列表
- 对比过滤列表,移除不满足自动装载的类
- 封装返回
2.4.2.过程
process()方法中会调用getAutoConfigurationEntry()方法,并将过滤列表传和ClassLoader传过去,在getCandidateConfigurations()方法中通过传递的ClassLoader获取自动装配的列表"META-INF/spring.factories",然后比对过滤列表,将满足条件的待装配类的全路径记录在AutoConfigurationImportSelector.AutoConfigurationGroup的一个叫autoConfigurationEntries的List中。
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); //从spring.factories中加载所有自动配置类 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); //移除重复配置类 configurations = removeDuplicates(configurations); //得到指定要移除的类(@SpringBootApplication(exclude=FreeMarkerAutoConfiguration.class)) Set<String> exclusions = getExclusions(annotationMetadata, attributes); //检查指定要移除的类,如果不是配置类,抛出异常 checkExcludedClasses(configurations, exclusions); //移除指定要移除的自动配置类 configurations.removeAll(exclusions); //获取满足条件的自动配置类列表 configurations = filter(configurations, autoConfigurationMetadata); //记录下符合条件的对象,并封装在实体中返回 fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
一切执行完毕后会回到入口出继续向下执行this.group.selectImports(),最终会调用到AutoConfigurationImportSelector的selectImports()方法,在该方法中会根据@order对自动配置类进行排序。
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()); }