【轉】Spring Boot run方法執行流程

今日看到一篇不錯的文章,轉載過來。

原文:Spring Boot run方法執行流程


SpringApplication的run方法的實現是啓動原理探尋的起點,該方法的主要流程大體可以歸納如下:

1、如果我們使用的是SpringApplication的靜態run方法,那麼,這個方法裏面首先要創建一個SpringApplication對象實例,然後調用這個創建好的SpringApplication的實例方法。在SpringApplication實例初始化的時候,它會提前做幾件事情:

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
                                                 String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }
  • 根據classpath裏面是否存在某個特徵類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應該創建一個爲Web應用使用的ApplicationContext類型。
  • 使用SpringFactoriesLoader在應用的classpath中查找並加載所有可用ApplicationContextInitializer。

  • 使用SpringFactoriesLoader在應用的classpath中查找並加載所有可用的ApplicationListener。

  • 推斷並設置main方法的定義類。

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 = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //把SpringdemoApplication.class設置爲屬性存儲起來
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        //設置應用類型爲Standard還是Web
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //設置初始化器(Initializer),最後會調用這些初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //設置監聽器(Listener)
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

2、SpringApplication實例初始化完成並且完成設置後,就開始執行run方法的邏輯了,方法執行依次,

首先遍 歷執行所有通過SpringFactoriesLoader可以查找到並加載的SpringApplicationRunListener。

調用它們的 started()方法,告訴這些SpringApplicationRunListener,“嘿,SpringBoot應用要開始執行咯!”。

/*從run方法進來*/
    public ConfigurableApplicationContext run(String... args) {
        //計時工具
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        //第一步,獲取並啓動監聽器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //第二步,根據SpringApplicationRunListeners以及參數來準備環境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            //準備Banner打印器‐就是啓動Spring Boot的時候在console上的ASCII藝術字體
            Banner printedBanner = this.printBanner(environment);
            //第三步:創建Spring容器
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            //第四步:Spring容器前置處理
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //第五步:刷新容器
            this.refreshContext(context);
            //第六步:Spring容器後置處理
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
            //第七步:發出結束執行的事件
            listeners.started(context);
            //第八步:執行Runners
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            //返回容器
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

 

3、創建並配置當前Spring Boot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile)。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach((Environment)environment);
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

4、遍歷調用所有SpringApplicationRunListener的environmentPrepared()的方法,告訴他們:“當前SpringBoot應用使用的Environment準備好了咯!”。

void environmentPrepared(ConfigurableEnvironment environment) {
        Iterator var2 = this.listeners.iterator();
        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
            listener.environmentPrepared(environment);
        }

    }

5、如果SpringApplication的showBanner屬性被設置爲true,則打印banner。

public interface Banner {
    void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);
    public static enum Mode {
        OFF,
        CONSOLE,
        LOG;
        private Mode() {
        }
    }
}

6、根據用戶是否明確設置了applicationContextClass類型以及初始化階段的推斷結果,決定該爲當前SpringBoot應用創建什麼類型的ApplicationContext並創建完成,

然後根據條件決定是否添ShutdownHook,決定是否使用自定義的BeanNameGenerator,

決定是否使用自定義的ResourceLoader,當然,最重要的,將之前準備好的Environment設置給創建好的ApplicationContext使用。

 

7、ApplicationContext創建好之後,SpringApplication會再次藉助Spring-FactoriesLoader,查找並加載classpath中所有可用的ApplicationContext-Initializer,

然後遍歷調用這些ApplicationContextInitializer的initialize(applicationContext)方法來對已經創建好的ApplicationContext進行進一步的處理。

protected void applyInitializers(ConfigurableApplicationContext context) {
        Iterator var2 = this.getInitializers().iterator();

        while(var2.hasNext()) {
            ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }

    }

8、遍歷調用所有SpringApplicationRunListener的contextPrepared()方法。

9、最核心的一步,將之前通過@EnableAutoConfiguration獲取的所有配置以及其他形式的IoC容器配置加載到已經準備完畢的ApplicationContext

10、遍歷調用所有SpringApplicationRunListener的contextLoaded()方法。

void contextLoaded(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextLoaded(context);
        }
    }

11、調用ApplicationContext的refresh()方法,完成IoC容器可用的最後一道工序。

private void refreshContext(ConfigurableApplicationContext context) {
        this.refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }

    }

12、查找當前ApplicationContext中是否註冊有CommandLineRunner,如果有,則遍歷執行它們。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();

        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);
            }
        }
    }

13、正常情況下,遍歷執行SpringApplicationRunListener的finished()方法、(如果整個過程出現異常,則依然調用所有SpringApplicationRunListener的finished()方法,只不過這種情況下會將異常信息一併傳入處理) 去除事件通知點後,整個流程如下:

    void failed(ConfigurableApplicationContext context, Throwable exception) {
        for (SpringApplicationRunListener listener : this.listeners) {
            callFailedListener(listener, context, exception);
        }
    }

 


 

 

 

 

SpringBoot 啓動類 @SpringBootApplication 註解 以及執行流程

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章