new PackageImports(metadata).getPackageNames()
一.前言

SpringBoot项目的启动, 都需要如下的启动类

@SpringBootApplication
public class SpringBootDemo3Application {
	public static void main(String[] args) {
		SpringApplication.run(SpringBootDemo3Application.class, args);
	}
}

分析启动类, 可以看出核心是: 

  • 注解@SpringBootApplication
  • 方法SpringApplication.run(SpringBootDemo3Application.class, args)

分析SpringBoot的启动原理, 就需要从以上两处开始

二.注解@SpringBootApplication

点开注解, 源码如下

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

}

@SpringBootApplication是由三个注解组合而成( 原生JDK注解忽略 ), 分别是:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

即以上三个注解可以替代@SpringBootApplication

2.1 注解@SpringBootConfiguration

首先查看该注解源码

@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

该注解包含: 

  • @configuration : 该注解是Spring原生注解, 标注该类是Spring配置类, 可以在该类中向IOC容器添加Bean
  • @Indexed : 标识项目在编译打包的时候会在项目中自动生成META-INT/spring.components文件, 当SpringContext在执行ComponentScan扫描时,META-INT/spring.components将会被CandidateComponentsIndexLoader 读取并加载,转换为CandidateComponentsIndex对象,这样的话@ComponentScan不在扫描指定的package,而是读取CandidateComponentsIndex对象,从而达到提升性能的目的
  • proxyBeanMethods() default true : 外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象

2.2 注解@ComponentScan

作用 : @ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中

2.3 注解@EnableAutoConfiguration

作用 : 该注解是SpringBoot的核心注解, 借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

该注解包含

  • @AutoConfigurationPackage
  • @Import({AutoConfigurationImportSelector.class})

2.3.1 注解@AutoConfigurationPackage

查看源码, 该注解中包含@Import(AutoConfigurationPackages.Register.class) ,  通过@Import注解将Register导入到ioc容器中, 然后通过Register向IOC容器中导入一系列组件

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

   /**
    * Base packages that should be registered with {@link AutoConfigurationPackages}.
    * <p>
    * Use {@link #basePackageClasses} for a type-safe alternative to String-based package
    * names.
    * @return the back package names
    * @since 2.3.0
    */
   String[] basePackages() default {};

   /**
    * Type-safe alternative to {@link #basePackages} for specifying the packages to be
    * registered with {@link AutoConfigurationPackages}.
    * <p>
    * Consider creating a special no-op marker class or interface in each package that
    * serves no purpose other than being referenced by this attribute.
    * @return the base package classes
    * @since 2.3.0
    */
   Class<?>[] basePackageClasses() default {};

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

}
  • AnnotationMetadata metadata : 启动类

SpringBoot启动原理解析_查看源码

  • new packageImports(metadata).getPackageNames : 获得启动类的包名

SpringBoot启动原理解析_查看源码_02

  • BeanDefinitionRegistry registry

SpringBoot启动原理解析_查看源码_03

  • register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0])); : 将启动类的包( 包含子包 )所有标注了@Component相关注解的组件导入到IOC容器

2.3.2 @Import({AutoConfigurationImportSelector.class})

将AutoConfigurationImportSelector

  • 利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
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;
}
  • 利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
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());
}
  • META-INF/spring.factories位置来加载一个文件。 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories

SpringBoot启动原理解析_spring_04

SpringBoot启动原理解析_ide_05

2.4 注解小结

SpringBoot启动原理解析_查看源码_06