Springboot源码解析

0.源码环境搭建

下载地址

github下载地址(2.2.9.RELEASE版本)

注意事项

1.为什么选择2.2.9.RELEASE版本

2.2.9.RELEASE以上版本采用gradle进行编译打包, 我们希望采用maven的方式

2.主pom.xml报错解决方案

标签下添加:

<disable.checks>true</disable.checks>

3.环境依赖

jdk -- jdk8+
maven -- 3.5+

在 spring-boot-project/spring-boot-parent/pom.xml文件中,依赖了一个plugin --> maven-enforcer-plugin,它有两个属性值:requireJavaVersion --> [1.8,) 以及 requireMavenVersion --> [3.5.0,)

参考

1.准备

maven

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

启动类

@SpringBootApplication
public class SpringBootLearningApplication {

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

}

2.@SpringBootApplication

@Target(ElementType.TYPE) // 注解适用范围, TYPE表示注解可以描述在类、接口、注解、枚举上
@Retention(RetentionPolicy.RUNTIME) // 表示注解的生命周期
@Documented // 表示注解可以记录在javadoc中
@Inherited  // 表示注解可以被子类继承
@SpringBootConfiguration // 表明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能 (spring中有很多@Enable注解, 原理就是借助@Import等相关注解向spring容器注册相关的核心Bean)
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

可以看到 @SpringBootApplication 注解上加了两个核心的自定义注解:@SpringBootConfiguration 和 @EnableAutoConfiguration,下面将介绍这两个注解的作用

2.1 @SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

总结

这个注解上加上了@Configuration注解,作用是标明当类上使用当前注解时,为主配置类

2.2 @EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

	String[] excludeName() default {};

}

这个注解是自动配置的核心注解,看到这个注解上又加了两个自定义注解,分别是 :@AutoConfigurationPackage 和   @Import(AutoConfigurationImportSelector.class),@AutoConfigurationPackage注解用来标识自动配置的包,@Import(AutoConfigurationImportSelector.class)是自动配置的核心

2.2.1 @AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class) // 向spring容器中注入AutoConfigurationPackages.Registrar
public @interface AutoConfigurationPackage {

}

查看当前注解,可以看到,当前注解上添加了@Import(AutoConfigurationPackages.Registrar.class),由spring-ioc源码,我们知道@Import注解用于向spring容器注册相关bean!ConfigurationClassPostProcessor会解析主配置类(@Configuration注解的类),解析时,会委托ConfigurationClassParser进行解析,(这一部分spring-ioc源码已经分析)使用ConfigurationClassParser会递归解析主配置类中@Import注解(递归解析是指会解析注解中的注解,只要包含@Import注解,都会被解析),下面再次展示处理@Import注解的代码

ConfigurationClassParser#processImports(..)
// Collection<SourceClass> importCandidates --> 使用@Import注解引用的类
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    // 1.校验非空
    if (importCandidates.isEmpty()) {
        return;
    }
    // 2.跳过
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {
                // 3.判断@Import的类是否为ImportSelector实现类
                if (candidate.isAssignable(ImportSelector.class)) {
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    // 3.1 当@Import引入的类实现了ImportSelector, 进行实例化
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    // 3.2 回调Aware接口
                    ParserStrategyUtils.invokeAwareMethods(
                        selector, this.environment, this.resourceLoader, this.registry);
                    // 3.3 如果@Import引入的类实现了ImportSelector, 但是同时实现了 DeferredImportSelector 接口(DeferredImportSelector接口是ImportSelector的子接口), 调用deferredImportSelectorHandler#handle()处理
                    if (selector instanceof DeferredImportSelector) {
                        // 这里handle方法, 将 selector 封装成 DeferredImportSelectorHolder, 添加到paser的deferredImportSelectors 列表属性中等待后续处理
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        // 3.4 如果只是实现了@ImportSelector, 没有实现 eferredImportSelector, 则调用其selectImports方法, 向容器中引入需要注册的bean, 并且递归调用processImports
                        // 本质上, @ImportSelector, 返回了A.class, B.class 相当于 @Import({A.class, B.class}), 仍然需要继续解析A.class, B.class, 判断其是否实现 @ImportSelector接口, 或者ImportBeanDefinitionRegistrar接口等!!!【这个非常重要, spring-tx就是采用这种方式】
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                // 4.判断@Import的类是否为ImportBeanDefinitionRegistrar实现类
                // 【重点】aop的核心注解@EnableAspectJAutoProxy 中 @Import(AspectJAutoProxyRegistrar.class)
                // 	AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口!!!
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // Candidate class is an ImportBeanDefinitionRegistrar ->
                    // delegate to it to register additional bean definitions
                    // 4.1 当@Import引入的类实现了ImportBeanDefinitionRegistrar, 进行实例化
                    // spring-aop、spring-transaction注解版都使用了 ImportBeanDefinitionRegistrar
                    // 在spring-aop中: 在主配置类上添加@EnableAspectJAutoProxy注解, @EnableAspectJAutoProxy注解上使用了
                    // @Import(AspectJAutoProxyRegistrar.class), AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口
                    // 因此这里会实例化对象 AspectJAutoProxyRegistrar
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                        BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(
                        registrar, this.environment, this.resourceLoader, this.registry);
                    // 4.2 将ImportBeanDefinitionRegistrar的实现类实例化后添加到配置类的 importBeanDefinitionRegistrars属性中
                    // 等待后面处理
                    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());
                    // 5.如果@Import引入的类既不是 ImportSelector实现类, 也不是 ImportBeanDefinitionRegistrar实现类
                    // 将其封装成 ConfigurationClass, 递归解析, 后续会创建称为普通的bean对象
                    processConfigurationClass(candidate.asConfigClass(configClass));
                }
            }
        }
        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();
        }
    }
}
AutoConfigurationPackages.Registrar
// 作用: 注册(org.springframework.boot.autoconfigure.AutoConfigurationPackages, BasePackages)核心bean, bean中添加一个参数:
// packageName, 这里即为主配置类所在包名
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      register(registry, new PackageImport(metadata).getPackageName());
   }

   @Override
   public Set<Object> determineImports(AnnotationMetadata metadata) {
      return Collections.singleton(new PackageImport(metadata));
   }

}

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    if (registry.containsBeanDefinition(BEAN)) {
        BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
        ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
        constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
    }
    else {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(BasePackages.class);
        // 这里的packageNames是使用了@AutoConfigurationPackage注解的类所在的包名
        // @SpringBootApplication注解上使用了@EnableAutoConfiguration注解,
        // @EnableAutoConfiguration注解上使用了@AutoConfigurationPackage注解,
        // 所以这里取的packageNames是使用@SpringBootApplication类所在的包名, 即主启动类所在包名
        beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(BEAN, beanDefinition);
    }
}
总结
@AutoConfigurationPackage的作用就是向spring容器中注册了一个bean:BasePackages

2.2.2 AutoConfigurationImportSelector

@Import解析

@Import(AutoConfigurationImportSelector.class),这个是springboot自动配置的核心,由于前面的学习,我们这里再一次总结一下@Import注解的作用(当我们的配置类上使用了@Import注解),根据@Import导入的jclass,一共可能有四种情况:

1.实现ImportBeanDefinitionRegistrar 接口
将ImportBeanDefinitionRegistrar的实现类实例化后添加到配置类的 importBeanDefinitionRegistrars属性中, 后期会调用其registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)方法, 向spring容器中注册相关的核心bean
我们在spring-aop, spring-mybatis源码中有实现

spring-aop中,我们在主配置类中加了@EnableAspectJAutoProxy注解,,注解中引入了AspectJAutoProxyRegistrar

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	boolean proxyTargetClass() default false;

	boolean exposeProxy() default false;

}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

在spring-mybatis,注解方式中,我们在主配置类中添加了@MapperScan注解,注解中@Import(MapperScannerRegistrar.class)向spring容器中导入了MapperScannerRegistrar,而MapperScannerRegistrar则实现了MapperScannerRegistrar则实现了ImportBeanDefinitionRegistrar接口

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {

  String[] value() default {};

  String[] basePackages() default {};

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

  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

  Class<? extends Annotation> annotationClass() default Annotation.class;

  Class<?> markerInterface() default Class.class;

  String sqlSessionTemplateRef() default "";

  String sqlSessionFactoryRef() default "";

  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

}
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  private ResourceLoader resourceLoader;

  /**
   * {@inheritDoc}
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    // this check is needed in Spring 3.1
    if (resourceLoader != null) {
      scanner.setResourceLoader(resourceLoader);
    }

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      scanner.setAnnotationClass(annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

}
2.实现ImportSelector接口,但未实现DeferredImportSelector接口
将ImportSelector的实现类实例化后, 调用其String[] selectImports(AnnotationMetadata importingClassMetadata)方法, 向spring容器内导入相关的bean, 这里作用类似于@Import
我们在spring-transaction有实现

在spring-transaction中,我们在主配置类中加上了@EnableTransactionManagement注解,注解中引入了TransactionManagementConfigurationSelector,这个类实现了ImportSelector接口

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	boolean proxyTargetClass() default false;

	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;

}
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}

}

默认情况下,adviceMode == PROXY,所以导入了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration,这里相当于使用 @Import(AutoProxyRegistrar.class)和@Import(ProxyTransactionManagementConfiguration.class),我们仍然会递归处理AutoProxyRegistrar,ProxyTransactionManagementConfiguration,判断它们是否实现SelectImport接口,是否实现DefferedSelectorImport接口或是ImportBeanDefinitionRegistrar 接口等

3.实现实现ImportSelector接口,且实现DeferredImportSelector接口
这个是当前springboot中自动配置核心注解@EnableAutoConfiguration导入的AutoConfigurationImportSelector的情况
4.未实现上述所有接口
将其解析成配置类, 并处理
在spring-tx中有相关实现

在spring-tx中,由上述我们知道先通过第二种方式,向spring容器中导入了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration,其中 ProxyTransactionManagementConfiguration 就是普通的配置类!

DeferredImportSelector解析

由上述我们知道,当前springboot中向容器中导入了AutoConfigurationImportSelector,这个类实现了DeferredImportSelector接口,属于上述第三种情况,由上述 ConfigurationClassParser#processImports(..) 代码解析得知,这里会实例化 AutoConfigurationImportSelector,回调其相关Aware接口方法,并添加到 ConfigurationClassParser 的 deferredImportSelectors 列表属性中等待后续处理

ConfigurationClassParser#parse(Set configCandidates)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    // 1.解析所有的配置类
    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);
        }
    }
	// 2.解析完所有的配置类后, 开始解析 deferredImportSelectors!!!
    this.deferredImportSelectorHandler.process();
}
DeferredImportSelectorHandler#process()
public void process() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        // 1.这里 deferredImports!= null, 这里有 AutoConfigurationImportSelector 对应的 DeferredImportSelectorHolder
        if (deferredImports != null) {
            // 2.创建 DeferredImportSelectorGroupingHandler
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            // 3.调用 handler#register(DeferredImportSelectorHolder deferredImport)方法
            deferredImports.forEach(handler::register);
            // 4.【核心】处理, 完成自动装配 
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}
DeferredImportSelectorGroupingHandler##register(DeferredImportSelectorHolder deferredImport)
public void register(DeferredImportSelectorHolder deferredImport) {
    // 1.获取 AutoConfigurationImportSelector 的 importGroup --> AutoConfigurationImportSelector$AutoConfigurationGroup
    Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
    // 2.创建 DeferredImportSelectorGrouping, 添加到 groupings 中
    DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
        (group != null ? group : deferredImport),
        key -> new DeferredImportSelectorGrouping(createGroup(group)));
    grouping.add(deferredImport);
    // 3.添加到 configurationClasses
    this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
                                  deferredImport.getConfigurationClass());
}
DeferredImportSelectorGroupingHandler#processGroupImports()
public void processGroupImports() {
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        // 1.这里 this.groupings.values() 即为上述创建的 DeferredImportSelectorGrouping
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        // 2.grouping.getImports() 特别重要, 获取需要自动装配的配置类(读取 spring.factories配置文件 并完成过滤)
        grouping.getImports().forEach(entry -> {
            ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
            try {
                // 3.每个entry都对应着自动配置类, 这里递归处理自动配置类, 继续判断当前类是否实现 @ImportSelector、@ImportBeanDefinitionRegistry等
                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);
            }
        });
    }
}
DeferredImportSelectorGrouping#getImoprts()
public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        // 1.process --> 这个方法及其重要, 它会扫描jar包内所有的 /META-INF/spring.factories 配置文件, 读取org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ , 下的所有配置, 一共124个自动配置类的全限定类名, 并进行过滤, 过滤条件即为 自动配置类的@ConditionalOnxxx条件是否满足, 只有满足条件的才会启用!!!
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                           deferredImport.getImportSelector());
    }
    // 2.getImports() --> 过滤并排序
    return this.group.selectImports();
}

@ConditionalOnxxx

@ConditionalOnBean: 在当前spring容器中存在某个bean, 才会实例化当前Bean
@ConditionalOnClass: 当类路径下存在某个类的Class文件时, 才会实例化当前Bean
@ConditionalOnExpression: 当条件表达式为true时, 才会实例化当前Bean
@ConditionalOnMissingBean: 当spring容器中不存在某个bean, 才会实例化当前Bean
@ConditionalOnMissingClass: 当类路径下不存在某个类的Class文件时, 才会实例化当前Bean
@ConditionalOnProperty: 当相关属性匹配时, 才会实例化当前Bean
总结
@Import(AutoConfigurationImportSelector.class)注解的作用是, 向容器中导入了 AutoConfigurationImportSelector, 它是DefferedImportSelector的实现类, spring会实例化AutoConfigurationImportSelector, 同时实例化其内部类AutoConfigurationGroup, 调用其process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)方法读取 META-INF/spring.factories配置文件, 读取自动配置类, 进行过滤; 最终递归解析过滤后的自动配置类, 完成扫描和注册!!!

3.SpringApplication#run解析

3.1 SpringApplication#run(Class<?> primarySource, String... args)

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
   // 调用其重载方法
   return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

总结

调用run方法, 核心方法包括两部分: 
第一: new SpringApplication(Class clazz) , 构造方法, 入参是启动类, 在@3.2中介绍
第二: run(args) 方法, 在@3.3中介绍

3.2 SpringApplication(Class<?>... primarySources)

public SpringApplication(Class<?>... primarySources) {
    // 调用其构造方法 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
    this(null, primarySources);
}

这里重点看 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)

3.2.1 SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 1.赋值 resourceLoader, 一般为null
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 2.赋值 primarySources, 为启动类对应的Class
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 3.推断web应用类型, 有三个值: REACTIVE、SERVLET、NONE, 这里主要根据引用的jar包来判断, 见@3.2.2
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 4.getSpringFactoriesInstances(ApplicationContextInitializer.class)很重要: 这里会读取
    // 所有 META-INF/spring.factories 配置文件, 获取里面所有key == org.springframework.context.ApplicationContextInitializer, 进行实例化, 一般有7个, 实例化之后存到initializer属性中, 它们的作用暂略
    // 见@3.2.3 
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 5.同4, 这里读取的key == ApplicationListener, 是spring事件监听器 
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 6.推断main方法的类名, 即启动类, 赋值到 mainApplicationClass 属性中
    this.mainApplicationClass = deduceMainApplicationClass();
}

3.2.2 SpringApplication#deduceFromClasspath()

/**
 * 推断web应用类型 -- REACTIVE、 SERVLET、 NONE(非web容器)
 * @return
 */
static WebApplicationType deduceFromClasspath() {
    // 1.使用类加载器加载对应的类
    // 能加载到: org.springframework.web.reactive.DispatcherHandler
    // 且不能加载到: org.springframework.web.servlet.DispatcherServlet, org.glassfish.jersey.servlet.ServletContainer
    // 满足以上三个条件, 即为REACTIVE
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
        && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    // 2.加载不到 javax.servlet.Servlet 或 org.springframework.web.context.ConfigurableWebApplicationContext
    // 为NONE
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    // 3.不满足以上条件, 则为SERVLET, 这是springmvc环境中的
    return WebApplicationType.SERVLET;
}

3.2.3 SpringApplication#getSpringFactoriesInstances(Class type)

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    // 调用重载方法
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    // 1.获取类加载器
    ClassLoader classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates
    // 2.读取 META-INF/spring.factories配置文件中, 根据key取相关的value
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 3.实例化读取的全限定类名的实例
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    // 4.排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

总结

SpringApplication构造方法中, 主要:
1.推断当前spring应用的类型, 类型有三个: reactive, servlet, none, 推断的依据是根据当前应用所引用的spring相关jar包中的类, 如引用springmvc相关jar包, 则为servlet
2.从 META-INF/spring.factories 配置文件中读取两个key对应的value:
ApplicationContextInitializer 和 ApplicationListener
3.推断main函数的启动类
4.将以上信息存储到SpringApplication对应的属性上

3.3 SpringApplication#run(String... args)

// 创建应用并启动
public ConfigurableApplicationContext run(String... args) {
    // 1.记录运行时间, 暂略
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    // 2.获取SpringApplicationRunListeners监听器
    // 这里也是通过读取 META-INF/spring.factories配置文件, 读取 key == org.springframework.boot.SpringApplicationRunListener
    // 对应的value, 这里只有一个
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 3.启动监听器 -- 这里的监听器是事件发布器
    // 这里会发布 ApplicationStartingEvent 应用启动等相关事件
    // 监听器监听事件后会进行对应处理, 监听器是在构造器中读取spring.factories配置文件, 进行实例化并存储在当前SpringApplication中的
    // 如: 这里有一个 ConfigFileApplicationListener监听到此事件后, 会读取主配置文件 application.yml文件等
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 4.构建 ConfigurableEnvironment, 这里会根据当前应用的类型创建不同类型的Environment对象
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 5.打印spring图标
        Banner printedBanner = printBanner(environment);
        // 6.创建 ApplicationContext
        // 根据当前应用类型创建不同类型的ApplicationContext的子类:
        // servlet --> AnnotationConfigServletWebServerApplicationContext
        // reactive --> AnnotationConfigReactiveWebServerApplicationContext
        // none --> AnnotationConfigApplicationContext(这个是我们前期spring-ioc源码介绍中的核心类)
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] { ConfigurableApplicationContext.class }, context);
        // 7.ApplicationContext 准备阶段, 见@3.3.1 
        // 这里将启动类封装成BeanDefinition, 注册到spring容器的 beanDefinitionMap 中
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 8.【核心方法】refresh, 这里解析就是spring-ioc中的启动核心部分, 启动类上加了@SpringBootApplication中有@Configuration注解, 所以这里启动类会被封装成 主配置类 ConfigurationClass 进行解析!
        refreshContext(context);
        // 9.运行时间记录停止
        afterRefresh(context, applicationArguments);
        // 10.发布容器启动完成事件
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

3.3.1 SpringApplication#prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner)

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 1.设置 environment
    context.setEnvironment(environment);
    // 2.设置beanFactory的 ConversionService 属性
    postProcessApplicationContext(context);
    // 3.调用前期从 spring.factories 配置文件中读取的 ApplicationContextInitializer 实例化后的 initialize方法
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 4.创建并注册启动类的BeanDefinition到spring容器中BeanDefinitionMap中
    load(context, sources.toArray(new Object[0]));
    listeners.contextLoaded(context);
}

总结

这里主要跟spring-ioc源码类似, 有两点不同:
1.这里的主配置类@Configuration对应的类为启动类
2.启动类中的@SpringBootApplication注解中引用了 AutoConfigurationImportSelector , 这个类实现了DefferedImportSelector接口, 完成了META-INF/spring.factories中自动配置类的扫描和初始化!

4.SpringBoot Starter机制

4.1 概念

springboot中的starter是一种非常重要的机制, 能够抛弃以前繁杂的配置, 将其统一集成进starter, 应用者只需要在maven中引入starter依赖, springboot就能自动扫描到要加载的信息并启动相应的默认配置。springboot会自动通过classpath路径下的类发现需要的bean, 并注册进ioc容器

4.2 自定义starter

springboot内置starter:spring-boot-starter-xxx

自定义starter:xxx-spring-boot-starter

5.内嵌tomcat

5.1 spring-boot-starter-web

我们在项目中引入了 spring-boot-starter-web 包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

查看 spring-boot-starter-web 包,我们知道 spring-boot-starter-xxx 包中一般不会有java代码,都是引入spring和相关第三方包,并通过spring-boot自动配置机制,完成相关核心bean的注入

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-json</artifactId>
    </dependency>
    <!-- spring-boot-starter-web 默认采用tomcat作为web容器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-el</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>
</dependencies>

5.2 Tomcat内嵌原理

5.2.1 说明

由之前的学习我们知道,springboot自动配置原理中,在ApplicationContext启动时,会调用核心方法refresh(),会使用DefferedImportSelector的实现类 --> AutoConfigurationImportSelector及其内部类 AutoConfigurationGroup 去读取所有依赖jar包中的 META-INF/spring.factories 配置文件,获取key == org.springframework.boot.autoconfigure.EnableAutoConfiguration的实现类,通过实现类中的@ConditionalOnxxx进行过滤,判断当前类是否需要注册到spring中!!!

其中就包含了 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration和org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

5.2.2 ServletWebServerFactoryAutoConfiguration

Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

    ........   
}

查看其@Import注解,它向容器中导入了 ServletWebServerFactoryConfiguration的内部类 EmbeddedTomcat、EmbeddedJetty和EmbeddedUndertow三个servlet容器

5.2.3 ServletWebServerFactoryConfiguration

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedTomcat {

        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(
            ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
            ObjectProvider<TomcatContextCustomizer> contextCustomizers,
            ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            factory.getTomcatConnectorCustomizers()
                .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatContextCustomizers()
                .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatProtocolHandlerCustomizers()
                .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

    }

    /**
	 * Nested configuration if Jetty is being used.
	 */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedJetty {

        @Bean
        JettyServletWebServerFactory JettyServletWebServerFactory(
            ObjectProvider<JettyServerCustomizer> serverCustomizers) {
            JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
            factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

    }

    /**
	 * Nested configuration if Undertow is being used.
	 */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedUndertow {

        @Bean
        UndertowServletWebServerFactory undertowServletWebServerFactory(
            ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
            ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
            UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
            factory.getDeploymentInfoCustomizers()
                .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

    }

}

通过上述可以看到具体使用哪个servlet容器,也是通过@ConditionalOnxxx注解实现的,因为spring-boot-starter-web中默认使用了 tomcat,所以这里向容器中注入了 TomcatServletWebServerFactory,这个类是tomcatserver的工厂类,用于创建tomcat实例

5.3.4 DispatcherServletAutoConfiguration

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 配置加载顺序
@Configuration(proxyBeanMethods = false) // 不需要进行 cglib 代理
@ConditionalOnWebApplication(type = Type.SERVLET) // Servlet 应用的类型才注入当前 Bean
@ConditionalOnClass(DispatcherServlet.class) // 存在 DispatcherServlet 这个类才注入当前 Bean
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) // 配置加载顺序, 需要在ServletWebServerFactoryAutoConfiguration加载之后
public class DispatcherServletAutoConfiguration {

    /*
	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/"
	 */
    public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

    /*
	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
	 */
    public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

    @Configuration(proxyBeanMethods = false)
    @Conditional(DefaultDispatcherServletCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
    protected static class DispatcherServletConfiguration {

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
            dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
            dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
            return dispatcherServlet;
        }

    }

    @Configuration(proxyBeanMethods = false)
    @Conditional(DispatcherServletRegistrationCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)
    @Import(DispatcherServletConfiguration.class)
    protected static class DispatcherServletRegistrationConfiguration {

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) // 为 DispatcherServlet 定义一个 RegistrationBean 对象,目的是往 ServletContext 上下文中添加 DispatcherServlet
        @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) // 需要存在名称为 `dispatcherServlet` 类型为 DispatcherServlet 的 Bean
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
                 WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,                                          webMvcProperties.getServlet().getPath());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }

    }

   
      ...省略...   
}
这是另一个自动配置的核心类, 可以看到当同时满足 @ConditionalOnWebApplication(type = Type.SERVLET), @ConditionalOnClass(DispatcherServlet.class) 这两个条件后, 就会向spring中注册DispatcherServletAutoConfiguration, 同时其中有两个内部类, DispatcherServletConfiguration, DispatcherServletRegistrationConfiguration, 这两个内部类有两个@Bean方法, 它们分别向spring容器中注册了  DispatcherServlet、 DispatcherServletRegistrationBean, DispatcherServletRegistrationBean负责将DispatcherServlet注册到springmvc容器中

总结

1.首先我们在项目中导入 spring-boot-starter-web , 它引用了tomcat相关jar
2.在SpringApplication.run()方法中, 会根据当前应用的类型, 创建对应的ApplicationContext上下文, 并调用其refresh()方法, 由前面分析我们知道, refresh()方法中借助 AutoConfigurationImportSelector 完成了自动配置, 其核心是读取所有jar中的 META-INF/spring.factories 配置文件, 并根据@ConditionalOnxxx 注解判断当前spring容器是否需要注入当前bean, 我们在引用spring-boot-starter-web后, 会向spring容器中引入 ServletWebServerFactoryAutoConfiguration, 在这个类上又有@Import注解, 根据具体依赖的servlet容器, 判断具体引入的ServerFactory, 默认会向容器中导入 TomcatServletWebServerFactory, 它用于创建tomcat实例
3.同时, 向spring容器中注册了 DispatcherServlet 和 DispatcherServletRegistrationBean, 其中DispatcherServletRegistrationBean实现了 ServletContextInitializer 接口

5.3 tomcat启动

5.3.1 说明

由上述,我们知道调用SpringApplication#run方法时会创建ApplicationContext对象(这里的ApplicationContext实现类为:AnnotationConfigServletWebServerApplicationContext),并向容器中导入TomcatServletWebServerFactory,用于创建tomcat容器,那么tomcat容器何时创建并启动?

5.3.2 SpringApplication#run(String... args)

// 创建应用并启动
public ConfigurableApplicationContext run(String... args) {
    // 1.记录运行时间, 暂略
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    // 2.获取SpringApplicationRunListeners监听器
    // 这里也是通过读取 META-INF/spring.factories配置文件, 读取 key == org.springframework.boot.SpringApplicationRunListener
    // 对应的value, 这里只有一个
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 3.启动监听器 -- 这里的监听器是事件发布器
    // 这里会发布 ApplicationStartingEvent 应用启动等相关事件
    // 监听器监听事件后会进行对应处理, 监听器是在构造器中读取spring.factories配置文件, 进行实例化并存储在当前SpringApplication中的
    // 如: 这里有一个 ConfigFileApplicationListener监听到此事件后, 会读取主配置文件 application.yml文件等
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 4.构建 ConfigurableEnvironment, 这里会根据当前应用的类型创建不同类型的Environment对象
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 5.打印spring图标
        Banner printedBanner = printBanner(environment);
        // 6.创建 ApplicationContext
        // 根据当前应用类型创建不同类型的ApplicationContext的子类:
        // servlet --> AnnotationConfigServletWebServerApplicationContext (springmvc使用的)
        // reactive --> AnnotationConfigReactiveWebServerApplicationContext
        // none --> AnnotationConfigApplicationContext(这个是我们前期spring-ioc源码介绍中的核心类)
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] { ConfigurableApplicationContext.class }, context);
        // 7.ApplicationContext 准备阶段, 见@3.3.1 
        // 这里将启动类封装成BeanDefinition, 注册到spring容器的 beanDefinitionMap 中
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 8.【核心方法】refresh, 这里解析就是spring-ioc中的启动核心部分, 启动类上加了@SpringBootApplication中有@Configuration注解, 所以这里启动类会被封装成 主配置类 ConfigurationClass 进行解析!, 见@5.3.3 
        refreshContext(context);
        // 9.运行时间记录停止
        afterRefresh(context, applicationArguments);
        // 10.发布容器启动完成事件
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

5.3.3 AnnotationConfigServletWebServerApplicationContext架构图

5.3.4 SpringApplication#refreshContext

private void refreshContext(ConfigurableApplicationContext context) {
    // 这里的context --> AnnotationConfigServletWebServerApplicationContext
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

SpringApplication#refresh

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    // 这里的ApplicationContext --> AnnotationConfigServletWebServerApplicationContext
    ((AbstractApplicationContext) applicationContext).refresh();
}

ServletWebServerApplicationContext#refresh

public final void refresh() throws BeansException, IllegalStateException {
    try {
        // 这里会调用 ioc 源码解析中的核心类 AbstractApplicationContext#refresh
        super.refresh();
    }
    catch (RuntimeException ex) {
        stopAndReleaseWebServer();
        throw ex;
    }
}

AbstractApplicationContext#refresh

这个方法我们在spring-ioc中已经解析过,这里重点看onRefresh()方法

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            // 这里采用模板方法设计模式, 由子类 AnnotationConfigServletWebServerApplicationContext实现, 该类继承了 ServletWebServerApplicationContext, 具体由其实现, 见@5.3.5
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

5.3.5 ServletWebServerApplicationContext#onRefresh

protected void onRefresh() {
    // 1.调用父类方法, 一般为空实现
    super.onRefresh();
    try {
        // 2.创建server, 这里会创建servlet容器!见@5.3.6
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

5.3.6 ServletWebServerApplicationContext#createWebServer

private void createWebServer() {
    // 1.这里 webServer == null
    WebServer webServer = this.webServer;
    // 2.这里 servletContext == null
    ServletContext servletContext = getServletContext();
    // 3.进入
    if (webServer == null && servletContext == null) {
        // 4.获取WebServerFactory, 这里会从spring容器中获取, 由前面学习我们知道, 在 ServletWebServerFactoryAutoConfiguration 中默认会向spring容器中注入 TomcatServletWebServerFactory, 用来创建 tomcat 容器, 所以这里的factory == TomcatServletWebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        // 5.这里分两步, 都很重要:
		// 5.1 getSelfInitializer() 见@5.3.7, 这里使用了jdk8新特性, 方法引用, 即返回一个匿名内部类对象
		// 5.2 factory#getWebServer 获取web容器, 并启动, 见@5.3.8 
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

5.3.7 ServletWebServerApplicationContext#getSelfInitializer

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    // 调用的是下面的方法
    return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
    // 1.将 servletContext 赋值到 WebApplicationContext属性上
    prepareWebApplicationContext(servletContext);
    // 2.ServletContextScope处理, 暂略
    registerApplicationScope(servletContext);
    // 3.将 ServletContext 及其相关属性对象注入到spring容器中
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    // 4.获取 ServletContextInitializer, 调用其startup方法
    // 这里会获取 之前注入的 DispatcherServletRegistrationBean, 并调用其onStartup方法, 向ServletContext中注入 DispatcherServlet
    // 以及其他 Servlet 和 Filter 等, 完成 Servlet, Filter 等的注册!!!
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

ServletWebServerApplicationContext#getServletContextInitializerBeans()

protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
    return new ServletContextInitializerBeans(getBeanFactory());
}

ServletContextInitializerBeans构造方法

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
                                      Class<? extends ServletContextInitializer>... initializerTypes) {
    this.initializers = new LinkedMultiValueMap<>();
    // 1.initializerTypes == ServletContextInitializer
    this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
        : Collections.singletonList(ServletContextInitializer.class);
    // 2.找到 spring 容器中所有 `ServletContextInitializer` 类型的 Bean, 并将这些信息添加到 `seen` 和 `initializers` 集合中
    addServletContextInitializerBeans(beanFactory);
    // 3.找到 spring 容器中所有 Servlet or Filter or EventListener 类型的 Bean, 适配成 RegistrationBean 对象, 并将这些信息添加到 `seen` 和 `initializers` 集合中
    addAdaptableBeans(beanFactory);
    List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
        .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
        .collect(Collectors.toList());
    this.sortedList = Collections.unmodifiableList(sortedInitializers);
    logMappings(this.initializers);
}

5.3.8 TomcatServletWebServerFactory#getWebServer

public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 1.创建tomcat对象
    Tomcat tomcat = new Tomcat();
    // 2.tomcat相关配置
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    } 
    prepareContext(tomcat.getHost(), initializers);
    // 3.创建 TomcatWebServer, 在其构造器方法中, 会调用init方法, 完成 tomcat容器的启动
    // 同时也会回调 initializers 的 onStartup方法, @5.3.7中的方法
    return getTomcatWebServer(tomcat);
}

原文:https://gitee.com/tca/spring-boot-2.2.9.-release