Spring Boot 啟動時做了什么


注解

@SpringBootApplication注解 中包括三個注解:

  • @EnableAutoConfiguration:借助@Import的幫助,將所有符合自動配置條件的bean定義加載到IoC容器
  • @Configuration:Spring Ioc容器的配置類,
  • @ComponentScan:組件掃描,可自動發現和裝配Bean,功能其實就是自動掃描並加載符合條件的組件或者bean定義,最終將這些bean定義加載到IoC容器中.默認掃描SpringApplication的run方法里的class所在的包路徑下文件,所以最好將該啟動類放到根包路徑下

啟動方法

創建SpringApplication實例

    public SpringApplication(Class... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    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");
        // 初始化主要加載資源類集合
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 判斷項目類型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 設置初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //設置監聽器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //推斷程序主類
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

初始化主要加載資源類集合

判斷項目類型

	public enum WebApplicationType {
		NONE,
	    SERVLET,
	    REACTIVE;
	
	    private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
	    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
	    private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
	    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
	    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
	    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
	
	    private WebApplicationType() {
	    }
	
	    static WebApplicationType deduceFromClasspath() {
	        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
	            return REACTIVE;
	        } else {
	            String[] var0 = SERVLET_INDICATOR_CLASSES;
	            int var1 = var0.length;
	
	            for(int var2 = 0; var2 < var1; ++var2) {
	                String className = var0[var2];
	                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
	                    return NONE;
	                }
	            }
	
	            return SERVLET;
	        }
	    }
	}

根據classpath中是否包含指定類
1.REACTIVE:響應式WEB項目 ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)
2.SERVLET:SERVLET WEB 項目 (!ClassUtils.isPresent("javax.servlet.Servlet", (ClassLoader)null)) && (!ClassUtils.isPresent("org.springframework.web.context.ConfigurableWebApplicationContext", (ClassLoader)null))
3.NONE:非WEB項目

設置應用上線文初始化器 ApplicationContextInitializer

路徑下META-INF/spring.factories文件ApplicationContextInitializer 接口的所有配置的類路徑名稱,用來初始化指定的 Spring 應用上下文,如注冊屬性資源、激活 Profiles 等。

  1. 獲取類加載器
  2. 獲取ApplicationContextInitializer實例名稱
  3. 創建初始化器實例
  4. 給初始化器實例排序

設置監聽器 ApplicationListener

路徑下META-INF/spring.factories文件,使用初始化器相同的方法設置

設置程序的主類

執行run方法

run方法流程

創建計時器

StopWatch stopWatch = new StopWatch();
stopWatch.start();

配置awt

this.configureHeadlessProperty();

private void configureHeadlessProperty() {
   System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
}

用來進行簡單的圖像處理,驗證碼生成等

獲取/啟動SpringApplicationRunListeners

SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();

與初始化器方式一致,掃描META-INF/spring.factories 文件中實現類進行創建

創建 ApplicationArguments

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

創建/初始化ConfigurableEnvironment

根據運行監聽器和應用參數來准備 Spring 環境

ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
	// 創建應用環境 
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    // 配置應用環境
    this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
    // TODO
    ConfigurationPropertySources.attach((Environment)environment);
    // 調用監聽器的environmentPrepared方法
    listeners.environmentPrepared((ConfigurableEnvironment)environment);
    this.bindToSpringApplication((ConfigurableEnvironment)environment);
    // TODO
    if (!this.isCustomEnvironment) {
        environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
    }

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

根據創建SpringApplication 實例時判斷的應用類型,創建實際的應用環境(Servlet 環境、響應式WEB環境和標准環境)

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    } else {
        switch(this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }
}

打印Banner

Banner printedBanner = this.printBanner(environment);

創建Banner,可以根據Banner原理客制化Banner。
根據參數獲取 spring.banner.image.location.
根據參數獲取 spring.banner.location banner.txt
以上都沒有,則打印SpringBootBanner
因此最簡單的方式則是在classpath下創建banner.txt文件
可以自定義banner網站:https://www.bootschool.net/ascii

創建應用上下文

context = this.createApplicationContext();

根據不同的應用類型創建不同的上下文

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch(this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                break;
            case REACTIVE:
                contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                break;
            default:
                contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
            }
        } catch (ClassNotFoundException var3) {
            throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
        }
    }

    return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

准備異常報告器

exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);

與初始化器方式一致,掃描META-INF/spring.factories 文件中實現類進行創建

准備應用上下文

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	// 設置上下文環境
    context.setEnvironment(environment);
    // 配置上下文bean生成器及加載器
    this.postProcessApplicationContext(context);
    // 執行初始化器
    this.applyInitializers(context);
    // 執行監聽器的contextPrepared方法
    listeners.contextPrepared(context);
    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 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]));
    // 觸發監聽器contextLoaded方法
    listeners.contextLoaded(context);
}

刷新應用上下文

this.refreshContext(context);

創建啟動內置服務器

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

}

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
    this.refresh((ConfigurableApplicationContext)applicationContext);
}

protected void refresh(ConfigurableApplicationContext applicationContext) {
    applicationContext.refresh();
}

刷新應用上下文后置

this.afterRefresh(context, applicationArguments);

目前此方法無實現,保留接口

停止計時器

stopWatch.stop();

輸出主類啟動時間

if (this.logStartupInfo) {
    (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}

觸發監聽器started

listeners.started(context);

執行Runner

this.callRunners(context, applicationArguments);

分為ApplicationRunner和CommandRunner,會在服務啟動時立即執行,可以在Runner中進行數據初始化等

觸發監聽器running方法

listeners.running(context);


免責聲明!

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



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