通常我们再使用 Spring Boot 的时候,这样简单的一段代码就实现了 Spring Boot 的启动,那么 Spring Boot 到底是如何实现的呢?

  1. @SpringBootApplication
  2. public class App {
  3. public static void main(String[] args) {
  4. SpringApplication.run(App.class, args);
  5. }
  6. }

@SpringBootApplication

我们先看 @SpringBootApplication 隐藏了那些细节

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @SpringBootConfiguration
  6. @EnableAutoConfiguration
  7. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  8. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  9. public @interface SpringBootApplication {
  10. }

@SpringBootConfiguration

首先我们发现了一个类似 @Configuration 的注解,的确他就是集成了 @Configuration 注解,那么就好办了,我们所定义的 App.class 其实也是一个@Configuration 的注解的 bean

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Configuration
  5. public @interface SpringBootConfiguration {
  6. }

@ComponentScan

这个注解不陌生了,就是定义包扫描路径,这里默认会扫描当前 App.class 所在的路径与其子包的所有 bean,但是我们并没有发现他定义了某个扫描路径,只是排除了一些 filter

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

@EnableAutoConfiguration

我们发现这个注解通过 @Import 注册了一个 AutoConfigurationImportSelector.class 的 类

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. @Import(AutoConfigurationImportSelector.class)
  7. public @interface EnableAutoConfiguration {
  8. }

AutoConfigurationImportSelector.class 又实现了 ImportSelector 接口,而 ImportSelector 接口会通过 selectImports 方法返回一个 String[] 数组,这个数组里面包含类的全限定名,并注册到 Spring 容器中去

  1. public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  2. ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  3. @Override
  4. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  5. if (!isEnabled(annotationMetadata)) {
  6. return NO_IMPORTS;
  7. }
  8. // 核心方法
  9. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  10. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  11. }
  12. }

我们可以看出来核心方法是 getAutoConfigurationEntry(annotationMetadata); 会返回一个配置信息

  1. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  2. if (!isEnabled(annotationMetadata)) {
  3. return EMPTY_ENTRY;
  4. }
  5. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  6. // 核心方法,从 spring.factory 中读取自动装配的信息
  7. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  8. configurations = removeDuplicates(configurations);
  9. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  10. checkExcludedClasses(configurations, exclusions);
  11. configurations.removeAll(exclusions);
  12. configurations = getConfigurationClassFilter().filter(configurations);
  13. fireAutoConfigurationImportEvents(configurations, exclusions);
  14. return new AutoConfigurationEntry(configurations, exclusions);
  15. }

获取候选的自动装配配置信息 getCandidateConfigurations(annotationMetadata, attributes); ,这里会返回 META-INF/spring.factories 中的所有信息作为候选对象,这里也是 Spring SPI 的体现

  1. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  2. List<String> configurations =
  3. SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
  4. getBeanClassLoader());
  5. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
  6. + "are using a custom packaging, make sure that file is correct.");
  7. return configurations;
  8. }

通过 getSpringFactoriesLoaderFactoryClass() 获取现在要加载的 FactoryClass,也就是 spring.factories 文件中的 key 对应的值

  1. protected Class<?> getSpringFactoriesLoaderFactoryClass() {
  2. return EnableAutoConfiguration.class;
  3. }

所以这里要加载的就是 EnableAutoConfiguration.class 的全限定名下的所有类

  1. # Auto Configure
  2. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  3. org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
  4. org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
  5. org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
  6. org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
  7. org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
  8. ...

@AutoConfigurationPackage

再跟到这个注解中,我们一样发现了 @Import 注册了一个 AutoConfigurationPackages.Registrar.class 的类,这个类实现了ImportBeanDefinitionRegistrar 接口,所以回调用 registerBeanDefinitions 方法来手工注册 bean

我们看一下 AutoConfigurationPackages.Registrar.class 的 registerBeanDefinitions 方法到底做了些什么?

  1. static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
  2. @Override
  3. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  4. register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
  5. }
  6. @Override
  7. public Set<Object> determineImports(AnnotationMetadata metadata) {
  8. return Collections.singleton(new PackageImports(metadata));
  9. }
  10. }

我们可以看到它通过 new PackageImports(metadata).getPackageNames().toArray(new String[0]) 来获取 packageName,而 packageName 如果我们不手工提供 @ComponentScan 注解的话,那么默认的 packageName 是如何来的呢,我们继续往下看

  1. private static final class PackageImports {
  2. private final List<String> packageNames;
  3. PackageImports(AnnotationMetadata metadata) {
  4. AnnotationAttributes attributes = AnnotationAttributes
  5. .fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
  6. List<String> packageNames = new ArrayList<>();
  7. for (String basePackage : attributes.getStringArray("basePackages")) {
  8. packageNames.add(basePackage);
  9. }
  10. for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
  11. packageNames.add(basePackageClass.getPackage().getName());
  12. }
  13. if (packageNames.isEmpty()) {
  14. packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
  15. }
  16. this.packageNames = Collections.unmodifiableList(packageNames);
  17. }
  18. }

我们可以看到,首先先找自定义的路径,最后如果没有找到的话,会使用当前 App.class 所在的根路径,这也就是为什么我们可以不定义 @ComponentScan ,那么默认的 packageName 就是当前 App.class 所在的同级目录及子目录的原因

SpringApplication.run

我们再看 SpringApplication.run(App.class, args); 这段代码,这段代码往下跟

  1. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  2. this.resourceLoader = resourceLoader;
  3. Assert.notNull(primarySources, "PrimarySources must not be null");
  4. // 将 App.class 赋值给了 this.primarySources
  5. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  6. this.webApplicationType = WebApplicationType.deduceFromClasspath();
  7. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  8. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  9. this.mainApplicationClass = deduceMainApplicationClass();
  10. }

this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 此时我们把 App.class 传递给了一个类变量 primarySources

我们继续跟 run 方法,其中有一个 prepareContext 方法中,会通过 load 来加载 App.class

  1. private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
  2. SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  3. context.setEnvironment(environment);
  4. postProcessApplicationContext(context);
  5. applyInitializers(context);
  6. listeners.contextPrepared(context);
  7. if (this.logStartupInfo) {
  8. logStartupInfo(context.getParent() == null);
  9. logStartupProfileInfo(context);
  10. }
  11. // Add boot specific singleton beans
  12. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  13. beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
  14. if (printedBanner != null) {
  15. beanFactory.registerSingleton("springBootBanner", printedBanner);
  16. }
  17. if (beanFactory instanceof DefaultListableBeanFactory) {
  18. ((DefaultListableBeanFactory) beanFactory)
  19. .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  20. }
  21. if (this.lazyInitialization) {
  22. context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
  23. }
  24. // Load the sources
  25. // 从 primarySources 中读取 App.class
  26. Set<Object> sources = getAllSources();
  27. Assert.notEmpty(sources, "Sources must not be empty");
  28. load(context, sources.toArray(new Object[0]));
  29. listeners.contextLoaded(context);
  30. }

然后调用 load 方法来加载类

  1. private int load(Object source) {
  2. Assert.notNull(source, "Source must not be null");
  3. if (source instanceof Class<?>) {
  4. return load((Class<?>) source);
  5. }
  6. if (source instanceof Resource) {
  7. return load((Resource) source);
  8. }
  9. if (source instanceof Package) {
  10. return load((Package) source);
  11. }
  12. if (source instanceof CharSequence) {
  13. return load((CharSequence) source);
  14. }
  15. throw new IllegalArgumentException("Invalid source type " + source.getClass());
  16. }

其底层就是将 App.class,注册到 Spring 容器中去

  1. private int load(Class<?> source) {
  2. if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
  3. // Any GroovyLoaders added in beans{} DSL can contribute beans here
  4. GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
  5. load(loader);
  6. }
  7. if (isEligible(source)) {
  8. // 将 App.class 注册到 Spring 容器中去
  9. this.annotatedReader.register(source);
  10. return 1;
  11. }
  12. return 0;
  13. }