Spring Boot启动流程

  1. SpringApplication初始化
  2. SpringApplication.run()
  3. 开始服务

SpringApplication初始化

  • 初始化流程:
    1. 配置resourceLoader
    2. 配置primarySources
    3. 配置webApplicationType
    4. 配置ApplicationContextInitialzer
    5. 配置ApplicationListener
    6. 配置mainApplicationClass
// Resource是Spring对物理资源的访问抽象
// ResourceLoader中只定义了一个用于获取Resource的getResource(String location)方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 设置在实例化SpringApplication时自定义的resourceLoader
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // primarySources 为 run 方法传入自定义的启动引导类(可以一次启动多个)
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推断web应用类
    /*
        根据 classpath下是否存在某个特征类来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
        1. 如果仅存在Reactive的包,则为WebApplicationType.REACTIVE类型;
        2. 如果Servlet和Reactive的包都不存在,则为WebApplicationType.NONE类型;
        3. 其他情况都为WebApplicationType.SERVLET类型。
    */
    this.webApplicationType = deduceWebApplicationType();
    // 初始化 initializers 属性
    /*
        1. 通过SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,在 META-INF/spring.factories文件下查找ApplicationContextInitializer类型对应的资源名称。
        2. 实例化上面的资源信息(初始化器)。
        3. 对初始化器根据Ordered接口或者@Order注解进行排序
    */
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    // 初始化监听器,与初始化initializers属性类似
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 推断应用引导类,调用栈中main方法所在的类作为引导类。
    this.mainApplicationClass = deduceMainApplicationClass();
}

启动入口

  • 包含两个核心组件:@SpringBootApplication注解和SpringApplication.run()静态方法。
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@SpringBootApplication

  • 该注解是一个复合注解。它并没有定义新的属性,而是复用和组合其他已有注解,可以开启自动配置、自动扫描的功能。
  • 包含三个核心注解:@Configuration、@ComponentScan、@EnableAutoConfiguration。
// Java中有四类元注解,@Target,@Retention,@Documented,@Inherited
// 注解的作用目标为接口、类、枚举
@Target(ElementType.TYPE)
// 注解的保留位置,会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
// 该注解将被包含在javadoc中
@Documented
// 子类可以继承父类中的该注解
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM,
				classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    /* 
        @AliasFor声明实现同一功能的不同属性,这些属性互为别名。
        通过@AliasFor,即使用户使用的是@SpringBootApplication,
        Spring还是可以通过AnnotationUtils#getAnnotation,           
        AnnotatedElementUtils#getMergedAnnotation等方法
        解析到@SpringBootConfiguration,@EnableAutoConfiguration, 
        @ComponentScan等注解,并取得对应属性。
    */
    @AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};
	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};
    // 根据包路径扫描
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};
    // 直接根据class类扫描
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};
}

@SpringBootConfiguration

  • 该注解继承自@Configuration,作用也相同,都是用来声明当前类是一个配置类。会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到Spring容器中,且实例名就是方法名。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;
}

@EnableAutoConfiguration

  • 该注解实现自动化配置,借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器。
  • 作用是从classpath中搜索所有META-INF/spring.factories配置文件,将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration key对应的配置项加载到spring容器,只有spring.boot.enableautoconfiguration为true(默认为true)时,才启用自动配置。也可以根据class来排除(exclude),或根据class name(excludeName)来排除。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
// @Import通过快速导入的方式实现把实例加入spring的IOC容器中
// ImportSelector接口的方法的返回值都会被纳入到spring容器管理中
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}
AutoConfigurationImportSelector.class
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // AnnotationMetadata可以获取当前类上注解的元数据,如注解名字,以及元注解信息等。从而判断是否开启了自动装配。
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    try {
        // 找到所有自动装配元数据属性文件,将所有配置信息加载到一个Properties对象中,AutoConfigurationMetadata可基于Properties实现。
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        // 获取注解的所有属性
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 利用SpringFactoriesLoader到classpath下的读取META-INF/spring.factories文件的配置,并返回一个字符串数组。
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        // 去重
        configurations = removeDuplicates(configurations);
        // 排序
        configurations = sort(configurations, autoConfigurationMetadata);
        // 排除
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        // 匹配过滤
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        // 返回值是实际上要导入到容器中的组件全类名
        return configurations.toArray(new String[configurations.size()]);
    }
    catch (IOException ex) {
        throw new IllegalStateException(ex);
    }
}

@ComponentScan

  • 该注解用于自动扫描被@Service/@Repository/@Component/@Controller注解标识的类,最终生成IoC容器的Bean。
  • 可以通过设置basePackages,includeFilters,excludeFilters属性来动态确定自动扫描范围,类型以及不扫描的类型。默认情况扫描所有类型。
  • 使用@SpringBootApplication注解,就使用了@ComponentScan的默认配置。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//在一个类中可重复定义
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    //定义扫描的包
    @AliasFor("basePackages")
    String[] value() default {};
    //定义扫描的包
    @AliasFor("value")
    String[] basePackages() default {};
    //定义扫描的类
    Class<?>[] basePackageClasses() default {};
    //Bean name生成器
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    //作用域解析器
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    //作用域代理模式
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    //资源匹配模式
    String resourcePattern() default "**/*.class";
    //是否启动默认的过滤器
    boolean useDefaultFilters() default true;
    //当满足过滤条件时扫描
    ComponentScan.Filter[] includeFilters() default {};
    //当不满足过滤条件时扫描
    ComponentScan.Filter[] excludeFilters() default {};
    //是否延迟初始化
    boolean lazyInit() default false;
    //自定义过滤器
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        //过滤器类型,可以按注解类型或者正则式等过滤
        FilterType type() default FilterType.ANNOTATION;
        //定义过滤的类
        @AliasFor("classes")
        Class<?>[] value() default {};
        //定义过滤的类
        @AliasFor("value")
        Class<?>[] classes() default {};
        //匹配方式
        String[] pattern() default {};
    }
}

SpringApplication.run()

  • 是SpringApplication的运行阶段,该阶段在初始化阶段完成的状态上,进一步完善运行时所需要准备的资源,随后启动Spring应用上下文,且在此期间伴随着SpringBoot和Spring事件的触发,形成SpringApplication完整的生命周期。
  • 运行流程:
    1. 计时器开始计时
    2. Headless模式复制
    3. 发送ApplicationStartingEvent
    4. 配置环境模块
    5. 发送ApplicationEnvironmentPreparedEvent
    6. 打印banner
    7. 创建应用上下文对象
      1. 初始化失败分析器
      2. 关联SpringBoot组件与应用上下文对象
      3. 发送ApplicationContextInitializedEvent
      4. 加载sources到context
      5. 发送ApplicationPreparedEvent
    8. 刷新上下文
      1. 获取beanFactory
      2. 准备beanFactory
      3. 调用BeanFactoryPostProcessors
      4. 注册BeanPostProcessors
      5. 初始化MessageSource
      6. 注册listener beans
      7. 实例化单例bean
      8. 发布对应事件
      9. 清除缓存
      10. 注册钩子方法
    9. 计时器停止计时
    10. 发送ApplicationStartedEvent
    11. 调用框架启动扩展类
    12. 发送ApplicationReadyEvent

SpringApplication准备阶段

  • 属于ApplicationContext启动前的阶段,范围从run()方法调用开始,到refreshContext()方法调用前。
public ConfigurableApplicationContext run(String... args) {
        //记录程序运行时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //Spring 应用的上下文
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 设置headless模式,是在缺少显示设备、键盘或鼠标的系统配置。
        configureHeadlessProperty();
        // 通过getSpringFactoriesInstances获取 META-INF/spring.factories 文件下SpringApplicationRunListener对应的资源
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            // 创建 ApplicationArguments 对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 加载属性配置
            /*
            1. 创建ConfigurableEnvironment对象。
            2. 配置environment变量。
            3. 发布ApplicationEnvironmentPreparedEvent事件。
其对应的监听器为ConfigFileApplicationListener,当接收到上面的事件时,加载并实例化 “META-INF/spring.factories” 文件中EnvironmentPostProcessor类型的实现类,遍历并执行其postProcessEnvironment方法。
            */
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            // 处理需要忽略的Bean
            configureIgnoreBeanInfo(environment);
            // 打印 banner
            Banner printedBanner = printBanner(environment);
            // 根据webApplicationType的类型,创建不同的Spring应用上下文
            context = createApplicationContext();
            // 实例化 SpringBootExceptionReporter,用来报告关于启动过程中的错误
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            // 应用上下文的准备阶段
            /*
            1. 给context的属性做赋值,如设置环境变量,调用初始化器来初始化context。
            2. 获取所有配置源信息,包括 Configuration Class、类名、包名及Spring XML 配置资源路径信息。
            3. 加载 Spring 应用上下文配置源。将BeanDefinition加载到context中。
            4. 发布上下文已准备事件ApplicationPreparedEvent。
            */
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // 刷新应用上下文(自动装配,初始化 IOC 容器)
            refreshContext(context);
            ...
        }
  }

Application启动阶段

  • 本阶段的执行由refreshContext(ConfigurableApplicationContext)完成,其核心方法是AbstractApplicationContext#refresh。
  • 随着该方法的执行,Spring Boot 核心特性也随之启动,如组件自动装配、嵌入式容器启动。
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备刷新
        prepareRefresh();
        // 通知子类刷新内部bean工厂
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 准备bean工厂以便在此上下文中使用
        prepareBeanFactory(beanFactory);
        try {
            // 允许上下文子类中对bean工厂进行后处理
            postProcessBeanFactory(beanFactory);
            // 在bean创建之前调用BeanFactoryPostProcessors后置处理方法
            invokeBeanFactoryPostProcessors(beanFactory);
            // 注册BeanPostProcessor
            registerBeanPostProcessors(beanFactory);
            // 注册DelegatingMessageSource
            initMessageSource();
            // 注册multicaster
            initApplicationEventMulticaster();
            // 创建内置的Servlet容器
            onRefresh();
            // 注册Listener
            registerListeners();
            // 完成BeanFactory初始化,初始化剩余单例bean
            finishBeanFactoryInitialization(beanFactory);
            // 发布对应事件
            finishRefresh();
        }
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);
            }
            // 销毁已经创建的单例,以避免悬空资源。
            destroyBeans();
            // 重置'active'标志
            cancelRefresh(ex);
            // 将异常传播给调用者。
            throw ex;
        }
        finally {
            // 在Spring的核心中重置常见的自省缓存,因为我们可能不再需要单例bean的元数据了
            resetCommonCaches();
        }
    }
}

ApplicationContext启动后阶段

public ConfigurableApplicationContext run(String... args) {
        ……
        // 该方法方法并未给 Spring 应用上下文启动后阶段提供实现,而是将其交给开发人员自行扩展
        afterRefresh(context, applicationArguments);、
        // 停止计时器
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // 发布 Spring 应用上下文已启动ApplicationStartedEvent事件
        listeners.started(context);
        // 调用实现了CommandLineRunner或者ApplicationRunner接口的类的 run 方法,得以满足需要在 Spring 应用上下文完全准备完毕后,执行一些操作的场景。
        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;
}

自动化装配

  1. 通过各种注解实现了类与类之间的依赖关系,容器在启动的时候Application.run,会调用EnableAutoConfigurationImportSelector.class的selectImports方法。
  2. selectImports方法最终会调用SpringFactoriesLoader.loadFactoryNames方法来获取一个全面的常用BeanConfiguration列表。
  3. loadFactoryNames方法会读取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigure.jar 下面的spring.factories),获取到所有的Spring相关的Bean的全限定名ClassName。
  4. selectImports方法继续调用filter(configurations, autoConfigurationMetadata);这个时候会根据这些BeanConfiguration里面的条件,来一一筛选,最关键的是
    @ConditionalOnClass,这个条件注解会去classpath下查找,jar包里面是否有这个条件依赖类,所以必须有了相应的jar包,才有这些依赖类,才会生成IOC环境需要的一些默认配置Bean。
  5. 最后把符合条件的BeanConfiguration注入默认的EnableConfigurationPropertie类里面的属性值,并且注入到IoC环境当中。