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);
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM