SpringBoot启动过程

研究SpringBoot的自动装配,同时也想弄明白它的启动流程,然后就有了这篇随笔。

SpringBoot的启动一般都是从main方法开始,这也是它的第一步

//SpringBoot注解,用于启动的
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
      //启动类静态的run方法
        SpringApplication.run(DemoApplication.class, args);
    }

}

第二步就是通过构造方法在创建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;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
       //通过调用deduceFromClasspath方法判断web应用是什么类型的
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
       //将启动注册初始化类通过工厂类得到实例放到ArrayList列表
        this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
       //将应用上下文初始化类通过工厂类得到实例进行初始化
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
       //ApplicationListener监听器类通过工厂类得到实例放进监听器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
      //得到main方法类实例
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

第三步就是会执行对应的run方法,run方法源码为:

 public ConfigurableApplicationContext run(String... args) {
   //获取启动时的时间毫秒数
        long startTime = System.nanoTime();
   //创建一个启动上下文容器
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
   //设置Headless模式属性,Headless模式模式代表当缺少外设依赖时使用此模式
        this.configureHeadlessProperty();
   //创建监听器,监听器开始启动
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
          //创建一个应用属性对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          //创建应用的上下文环境,从配置的application.properties等配置文件中读取
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
          //用来忽略所有自定义的BeanInfo类的搜索
            this.configureIgnoreBeanInfo(environment);
          //打印banner标志
            Banner printedBanner = this.printBanner(environment);
          //创建应用程序上下文
            context = this.createApplicationContext();
          //设置应用启动器
            context.setApplicationStartup(this.applicationStartup);
          //对bean,属性,标志进行初始化生成和处理
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
          //进行上下文刷新工作,如对系统关闭周期钩子重置内容等等
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
          //得到启动时长
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
          //告诉那个应用启动用了多少秒
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }
            //启动完成通知
            listeners.started(context, timeTakenToStartup);
          //如果有ApplicationRunner或者CommandLineRunner类型的bean,就会触发调用
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }

在上面的方法中有这一行代码  ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);  这一行代码涉及到了环境处理,属性设置等等。针对涉及到的this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);方法进行解析

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
  //根据web应用类型选择响应的应用容器环境
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
  //进行环境参数配置,但是此方法先去查看是否需要转换服务,再有条件地进行环境参数配置
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
  //通过判断环境中的是否含有onfigurationProperties的属性进行不同的处理
        ConfigurationPropertySources.attach((Environment)environment);
  //环境初始化,加载生效的profile等资源
        listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
  //配置属性中添加defaultProperties默认配置
        DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
  //判断环境属性中是否包含spring.main.environment-prefix键,如果包含的话,则会报出异常,异常内容第二个参数
        Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
  //将配置属性与各种组件进行绑定
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
  //判断是否需要进行容器类型转换
        if (!this.isCustomEnvironment) {
            environment = this.convertEnvironment((ConfigurableEnvironment)environment);
        }
//通过判断环境中的是否含有onfigurationProperties的属性进行不同的处理
        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

在上面run方法的源码中有这行代码this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);,查看这个方法源码

 private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
   //应用程序对自定义的加载器进行加载
        this.postProcessApplicationContext(context);
   //初始化上下文
        this.applyInitializers(context);
   //调用监听器
        listeners.contextPr epared(context);
   //往下追的话也是调用各种监听器
        bootstrapContext.close(context);
   //日志输出,输出了一行JDK信息和进程号,以及生效的profile模式内容
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }
//创建一个bean工厂
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   //利用工厂进行注册
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }
//进行懒加载
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
//加载资源
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }
生于忧患,死于安乐
原文地址:https://www.cnblogs.com/songlove/p/15647502.html