> 文章列表 > 【springboot】自动加载分析

【springboot】自动加载分析

【springboot】自动加载分析

文章目录

  • 问题
  • @SpringBootApplication注解
  • AutoConfigurationPackages.Registrar类
  • AutoConfigurationImportSelector类
  • springboot如何加载其他的starter
  • 总结

问题

为什么我们在使用springboot的时候,只需要在maven中导入starter就能够使用呢?这里来分析一下

@SpringBootApplication注解

这个注解一般用在springboot程序的主启动类上,这个注解除去元注解,由下面几个注解构成

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

@SpringBootConfiguration是由下面两个注解构成

@Configuration
@Indexed

@EnableAutoConfiguration是由下面内容构成

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage内容如下

@Import(AutoConfigurationPackages.Registrar.class)

所以@SpringBootApplication等价于下面内容

// @SpringBootConfiguration
@Configuration
@Indexed// @EnableAutoConfiguration
@Import(AutoConfigurationPackages.Registrar.class)
@Import(AutoConfigurationImportSelector.class)@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

可以发现实际就是导入了两个类,一个AutoConfigurationPackages.Registrar,一个AutoConfigurationImportSelector。


AutoConfigurationPackages.Registrar类

这个类的信息如下

	/* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing* configuration.*/static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}}

作用就是获取要扫描的包路径


AutoConfigurationImportSelector类

这个类的内容如下

【springboot】自动加载分析

里面实现了多个接口以Aware结尾的接口,其实就是完成某种资源的设置,Ordered就是用于指定bean加载顺序的,最重要的是DeferredImportSelector,这个接口继承了ImportSelector,ImportSelector就可以完成bean的注入工作

【springboot】自动加载分析

这个接口里面的group类里面就包含了要进行加载的bean信息

【springboot】自动加载分析

回到实现类,也就是AutoConfigurationImportSelector,去到process方法

@Override
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(annotationMetadata);this.autoConfigurationEntries.add(autoConfigurationEntry);for (String importClassName : autoConfigurationEntry.getConfigurations()) {this.entries.putIfAbsent(importClassName, annotationMetadata);}
}

将这个方法分解一下

   // 获取自动配置的类AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
   this.autoConfigurationEntries.add(autoConfigurationEntry);// 遍历自动配置的beanfor (String importClassName : autoConfigurationEntry.getConfigurations()) {// 如果存在这个类就进行加入this.entries.putIfAbsent(importClassName, annotationMetadata);}

里面关键的就是获取所有的自动配置类,关键方法就是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);

功能就是完成获取所有要进行自动加载的bean,进入到方法

	/* Return the auto-configuration class names that should be considered. By default* this method will load candidates using {@link ImportCandidates} with* {@link #getSpringFactoriesLoaderFactoryClass()}. For backward compatible reasons it* will also consider {@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 = new ArrayList<>(SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);Assert.notEmpty(configurations,"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;}

这个方法里面的

List<String> configurations = new ArrayList<>(SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));

【springboot】自动加载分析

这个方法用于读取类路径下的所有 META-INF/spring.factories文件

下面的这行代码

ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);

将其拆解为

ImportCandidates ic = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader());ic.forEach(configurations::add);

可以发现就是先去获取所有自动加载bean,然后将其加入到一个集合中,进入到load方法

	/* Loads the names of import candidates from the classpath. The names of the import candidates are stored in files named* {@code META-INF/spring/full-qualified-annotation-name.imports} on the classpath.* Every line contains the full qualified name of the candidate class. Comments are* supported using the # character.* @param annotation annotation to load* @param classLoader class loader to use for loading* @return list of names of annotated classes*/public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {Assert.notNull(annotation, "'annotation' must not be null");ClassLoader classLoaderToUse = decideClassloader(classLoader);String location = String.format(LOCATION, annotation.getName());Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);List<String> importCandidates = new ArrayList<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();importCandidates.addAll(readCandidateConfigurations(url));}return new ImportCandidates(importCandidates);}

里面最重要的语句就是

importCandidates.addAll(readCandidateConfigurations(url));

其实就是读取配置文件的信息,然后加入到集合中。读取的位置就是

【springboot】自动加载分析

META-INF/spring/%s.imports

实际上就是去读取

【springboot】自动加载分析

【springboot】自动加载分析

里面一共有144个默认加载项,然后再经过一些过滤操作,就会返回所有的配置类信息

	/* 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);}

【springboot】自动加载分析

后面springboot就会去处理这些这些配置类。


springboot如何加载其他的starter

其实上面已经说了,springboot会去读取类路径下的所有META-INF/spring.factories文件,所以只需要在这个文件中写上自动配置的类即可,以mybatis-plus为例子,如下

【springboot】自动加载分析


总结

springboot在启动的时候会去读取org\\springframework\\boot\\spring-boot-autoconfigure\\2.7.10\\spring-boot-autoconfigure-2.7.10.jar!\\META-INF\\spring\\org.springframework.boot.autoconfigure.AutoConfiguration.imports这个文件中的所有配置。

springboot通过读取META-INF\\spring.factories下的配置文件来支持其他starter的注入