SpringBoot启动原理

1.简述

  SpringBoot因为内置了tomcat或jetty服务器,不需要直接部署War文件,所以SpringBoot的程序起点是一个普通的主函数。

  主函数如下:

@SpringBootApplication
public class DemoApplication {

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

}
View Code

  SpringBoot的启动过程都是通过@SpringBootApplication注解和SpringApplication.run方法来实现的。

  启动的过程可以概括为

  1. 通过SpringFactoriesLoader加载META-INF/spring.factories文件,获取并创建SpringApplicationRunListener对象。
  2. 然后由SpringApplicationRunListener 来发出starting消息。
  3. 创建参数,并配置当前SpringBoot 应用将要使用的Environment。
  4. 完成之后,依然由SpringApplicationRunListener来发出environmentPrepared 消息。
  5. 创建ApplicationContext。
  6. 初始化ApplicationContext,并设置Environment,加载相关配置等。
  7. 由SpringApplicationRunListener来发出contextPrepared消息,告知SpringBoot应用使用的ApplicationContext已准备OK。
  8. 将各种beans装载入ApplicationContext,继续由SpringApplicationRunListener来发出contextLoaded消息,告知SpringBoot应用使用的ApplicationContext已装填OK。
  9. refresh ApplicationContext,完成IoC容器可用的最后一步
  10. 由SpringApplicationRunListener来发出started消息。
  11. 完成最终的程序的启动。
  12. 由SpringApplicationRunListener来发出running消息,告知程序已运行起来了。

2.源码分析

(1)@SpringBootApplication注解

  @SpringBootApplication注解的源码如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { //这两个排除过滤器TypeExcludeFilter和AutoConfigurationExcludeFilter不知道用来干什么的
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    //等同于EnableAutoConfiguration注解的exclude属性
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};
    //等同于EnableAutoConfiguration注解的excludeName属性
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};
    //等同于ComponentScan注解的basePackages属性
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};
    //等同于ComponentScan注解的basePackageClasses属性
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
}
View Code

可以看到@SpringBootApplication注解实际上是SpringBoot提供的一个复合注解,由以下三个注解组成

  • @SpringBootConfiguration:来源于 @Configuration,二者功能都是将当前类标注为配置类,并将当前类里以@Bean 注解标记的方法的实例注入到srping容器中。
  • @EnableAutoConfiguration:启用自动配置其可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前 IoC 容器之中。
  • @ComponentScan:对应于XML配置形式中的 context:component-scan,用于将一些标注了特定注解的bean定义批量采集注册到Spring的IoC容器之中,这些特定的注解大致包括:@Controller @Entity @Component @Service @Repository。

  因此@SpringBootApplication注解主要作为一个配置类,能够触发包扫描和自动配置的逻辑,从而使得SpringBoot的相关bean被注册进Spring容器。

(2)创建SpringApplication对象

  SpringApplication类的run方法,这个方法就做了2件事:一是创建SpringApplication对象,二是启动SpringApplication

  SpringApplication构造器源码如下

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = Collections.emptySet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.resourceLoader = resourceLoader;
    //断言primarySources不能为null,如果为null,抛出异常提示
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //启动类传入的Class
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //判断当前项目类型,有三种:NONE、SERVLET、REACTIVE
    this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class));
    //设置ApplicationContextInitializer
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //设置监听器
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    //判断主类,初始化入口类
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

//判断主类
private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
        StackTraceElement[] var2 = stackTrace;
        int var3 = stackTrace.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            StackTraceElement stackTraceElement = var2[var4];
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    } catch (ClassNotFoundException var6) {
    }

    return null;
}
View Code

  在构造器里主要就做了2件事,1是设置初始化器,2是设置监听器。

  设置初始化器的源码如下

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = this.getClassLoader();
    //从类路径的META-INF处读取相应配置文件spring.factories,然后进行遍历,读取配置文件中Key(type)对应的value
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //将names的对象实例化
    List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
View Code

  ApplicationContextInitializer.class从类路径的META-INF处读取相应配置文件spring.factories并实例化对应Initializer。

  设置监听器:和设置初始化器一个样,都是通过getSpringFactoriesInstances函数实例化监听器。

  创建了SpringApplication实例之后,就完成了SpringApplication类的初始化工作。

(3)run方法

  得到SpringApplication实例后,接下来就调用实例方法run()。

  run方法源码如下

public ConfigurableApplicationContext run(String... args) {
    //创建计时器
    StopWatch stopWatch = new StopWatch();
    //开始计时
    stopWatch.start();
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    //定义上下文对象
    ConfigurableApplicationContext context = null;
    //Headless模式设置
    this.configureHeadlessProperty();
    //加载SpringApplicationRunListeners监听器
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    //发送ApplicationStartingEvent事件
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        //封装ApplicationArguments对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //配置环境模块
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        //根据环境信息配置要忽略的bean信息
        this.configureIgnoreBeanInfo(environment);
        //打印Banner标志
        Banner printedBanner = this.printBanner(environment);
        //创建ApplicationContext应用上下文
        context = this.createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        //ApplicationContext基本属性配置
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        //刷新上下文
        this.refreshContext(context);
        //刷新后的操作,由子类去扩展
        this.afterRefresh(context, applicationArguments);
        //计时结束
        stopWatch.stop();
        //打印日志
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }

        //发送ApplicationStartedEvent事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
        listeners.started(context);
        //查找容器中注册有CommandLineRunner或者ApplicationRunner的bean,遍历并执行run方法
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        //发送ApplicationFailedEvent事件,标志SpringBoot启动失败
        this.handleRunFailure(context, var10, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        //发送ApplicationReadyEvent事件,标志SpringApplication已经正在运行,即已经成功启动,可以接收服务请求。
        listeners.running(context);
        return context;
    } catch (Throwable var9) {
        //报告异常,但是不发送任何事件
        this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}
View Code

  执行完run方法后,SpringBoot就启动完成了。

3.总结

  启动类看起来就一个@SpringBootApplication注解,一个run()方法。其实是经过高度封装后的。从这个分析中学到很多东西。例如使用了spring.factories文件来完成自动配置,提高了扩展性。在启动时使用观察者模式,以事件发布的形式通知,降低耦合,易于扩展等等。

posted on 2021-02-01 16:14  码农记录  阅读(1461)  评论(0编辑  收藏  举报

导航