溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Springboot Code的啟動源碼是怎樣的

發(fā)布時間:2021-09-29 13:56:28 來源:億速云 閱讀:120 作者:柒染 欄目:編程語言

Springboot Code的啟動源碼是怎樣的,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

項目啟動的流程:

(一)new SpringApplication

  1. 配置source和web環(huán)境;

  2. 創(chuàng)建初始化構造器和應用監(jiān)聽器;

  3. 配置應用的主方法所在類;

(二)run 第一二部分

1.獲取并啟動監(jiān)聽器

  • 初始化計時stopWatch、啟動上下文bootstrapContext、設置系統(tǒng)參數(shù)headless;

  • 初始化監(jiān)聽器列表SpringApplicationRunListeners;

  • 發(fā)布springboot開始啟動事件(從applicationListeners中過濾出4個能監(jiān)聽ApplicationStartingEvent事件的,并啟動它們)

2.準備環(huán)境

  • 裝配命令行參數(shù)applicationArguments(對象中裝載4個propertySource);

  • 準備應用程序運行的環(huán)境ConfigurableEnvironment(從applicationListeners中過濾出6個能監(jiān)聽ApplicationEnvironmentPreparedEvent事件的,并啟動它們。監(jiān)聽器中關聯(lián)啟動了一些后置處理器處理數(shù)據(jù),最終目的是為應用環(huán)境做準備)

3.打印banner

方法:

     public ConfigurableApplicationContext run(String... args) {
        //1、StopWatch簡單的看成一個stop watch的機制,保存stop的記錄信息。
        //初始化一個計時器,并開始計時
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //初始化啟動上下文
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        
        //2、configureHeadlessProperty即配置headless模式,這種模式是一種系統(tǒng)缺少顯示設備、鍵盤和鼠標外設的情況模式。
        this.configureHeadlessProperty();

        //3、SpringApplicationListeners為SpringApplicationRunListener接口實現(xiàn)集合(創(chuàng)建SpringApplicationRunListener初始化構造器)初始化監(jiān)聽器列表
        //可以理解這個接口就是在spring啟動整個過程都需要回調(diào)這些listener
        //debug能發(fā)現(xiàn),拿到了一個名為EventPublishingRunListener(RunListener構造方法中關聯(lián)上了全部applicationListener),這個就是用來進行觸發(fā)publishEvent的被觀察者
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //啟動EventPublishingRunListener,從而過濾并啟動相關Listener
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //4、ConfigurableEnvironment為配置環(huán)境對象,簡單理解所有的配置信息匯總在這個對象中
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);

            //5、Banner就是我們常在控制臺輸出的畫面橫幅,可以使用圖片或者文本進行替換
            Banner printedBanner = this.printBanner(environment);

    =====>  //6、ConfigurableApplicationContext根據(jù)webApp…Type進行構造的上下文對象
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

            //7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執(zhí)行所有的initializers邏輯,做初始化準備操作。
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

            //8、refreshContext,可以理解成容器初始化節(jié)點,將執(zhí)行bean的創(chuàng)建和實例化。
            this.refreshContext(context);

            //9、afterRefresh,容器后處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現(xiàn)類并執(zhí)行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啟動最后。
            this.afterRefresh(context, applicationArguments);

            //10、然后根據(jù)stopwatch打印出啟動時間
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
             
            //11、這里調(diào)用ApplicationRunner和CommandLineRunner的實現(xiàn)類
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

下面開始對context的創(chuàng)建與準備

    context = this.createApplicationContext();

    protected ConfigurableApplicationContext createApplicationContext() {
        return this.applicationContextFactory.create(this.webApplicationType);
    }
public interface ApplicationContextFactory {
    ApplicationContextFactory DEFAULT = (webApplicationType) -> {
        try {
            switch(webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
            }
        } catch (Exception var2) {
            throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);
        }
    };

    ConfigurableApplicationContext create(WebApplicationType webApplicationType);
    ....
}

上述代碼可以通過webApplicationTyep(即SERVLET)創(chuàng)建 AnnotationConfigServletWebServerApplicationContext 對象,對象創(chuàng)建過程中,初始化了父類的屬性值,其中有三個比較關鍵的值,reader、scanner和父類中的beanFactory,下面這個對象的層級結構需要了解一下

Springboot Code的啟動源碼是怎樣的

/**
 * reader 和 scanner 都是在構造方法中進行了賦值
**/
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
    private final AnnotatedBeanDefinitionReader reader;
    private final ClassPathBeanDefinitionScanner scanner;
    private final Set<Class<?>> annotatedClasses;
    private String[] basePackages;

    public AnnotationConfigServletWebServerApplicationContext() {
        this.annotatedClasses = new LinkedHashSet();
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }
    ...
}

我們可以在 GenericApplicationContext 類的構造方法中看到其構造方法中,對beanFactory屬性的賦值

    public GenericApplicationContext() {
        this.customClassLoader = false;
        this.refreshed = new AtomicBoolean();
        this.beanFactory = new DefaultListableBeanFactory();
    }

接下來對context進行配置與準備,看下prepareContext方法

//6、ConfigurableApplicationContext根據(jù)webApp…Type進行構造的上下文對象
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

    =====>  //7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執(zhí)行所有的initializers邏輯,做初始化準備操作。
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

            //8、refreshContext,可以理解成容器初始化節(jié)點,將執(zhí)行bean的創(chuàng)建和實例化。
            this.refreshContext(context);

            //9、afterRefresh,容器后處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現(xiàn)類并執(zhí)行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啟動最后。
            this.afterRefresh(context, applicationArguments);

            //10、然后根據(jù)stopwatch打印出啟動時間
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
             
            //11、這里調(diào)用ApplicationRunner和CommandLineRunner的實現(xiàn)類
            this.callRunners(context, applicationArguments);
	/**
	 * Spring容器準備
	 */
	private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
								ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
								ApplicationArguments applicationArguments, Banner printedBanner) {
		// 設置上下文環(huán)境
		context.setEnvironment(environment);
		//
		postProcessApplicationContext(context);
		// 執(zhí)行所有ApplicationContextInitializer對象的initialize方法(這些對象是通過讀取spring.factories加載)
		applyInitializers(context);
		// 發(fā)布上下文準備完成事件到所有監(jiān)聽器
		listeners.contextPrepared(context);
		bootstrapContext.close(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		//
		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());
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 加載bean到上下文
		load(context, sources.toArray(new Object[0]));
		// 發(fā)送上下文加載完成事件
		listeners.contextLoaded(context);
	}



    //為context設置beanFactoryPostProcess
    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
        if (this.beanNameGenerator != null) {
            context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator);
        }

        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext)context).setResourceLoader(this.resourceLoader);
            }

            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader());
            }
        }

        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }

    }

    //調(diào)用全部的構造器
    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);
        }

    }

    //加載class com.learning.demo.DemoApplication 啟動類
    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }

        BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }

        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }

        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }

        loader.load();
    }

debug,此處load方法可以跟進多層到 AnnotatedBeanDefinitionReader 的 registerBean(啟動類)

    public void registerBean(Class<?> beanClass) {
        this.doRegisterBean(beanClass, (String)null, (Class[])null, (Supplier)null, (BeanDefinitionCustomizer[])null);
    }

這里同樣有兩個listeners.*的兩個方法,和前面listeners.starting()是一樣的,提供下最近整理的類圖(了解Runlistener和listener類之間的關系)

Springboot Code的啟動源碼是怎樣的

//6、ConfigurableApplicationContext根據(jù)webApp…Type進行構造的上下文對象
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

            //7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執(zhí)行所有的initializers邏輯,做初始化準備操作。
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

     =====> //8、refreshContext,可以理解成容器初始化節(jié)點,將執(zhí)行bean的創(chuàng)建和實例化。
            this.refreshContext(context);

            //9、afterRefresh,容器后處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現(xiàn)類并執(zhí)行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啟動最后。
            this.afterRefresh(context, applicationArguments);

            //10、然后根據(jù)stopwatch打印出啟動時間
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
             
            //11、這里調(diào)用ApplicationRunner和CommandLineRunner的實現(xiàn)類
            this.callRunners(context, applicationArguments);

refreshContext() 是整個Run方法的核心部分

	/**
	 * 刷新應用程序上下文
	 *
	 * @param context
	 */
	private void refreshContext(ConfigurableApplicationContext context) {
		// 注冊一個關閉鉤子,在jvm停止時會觸發(fā),然后退出時執(zhí)行一定的退出邏輯
		if (this.registerShutdownHook) {
			try {
				// 添加:Runtime.getRuntime().addShutdownHook()
				// 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHook)
				context.registerShutdownHook();
			} catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
		// ApplicationContext真正開始初始化容器和創(chuàng)建bean的階段
		refresh((ApplicationContext) context);
	}
	
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
		refresh((ConfigurableApplicationContext) applicationContext);
	}

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

調(diào)用應用上下文對象的refresh()方法,接下來我i門到ConfigurableApplicationContext類中去看下這個方法

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
	void refresh() throws BeansException, IllegalStateException;
}

這是一個接口,且這個類是在spring框架中,非springboot,它的實現(xiàn)類共有三個

Springboot Code的啟動源碼是怎樣的

AbstractApplicationContext是一個抽象類,其余兩個類都繼承了它,我們來看看這個抽象類的代碼:

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// 第一步:準備更新上下時的預備工作
			prepareRefresh();

			// 第二步:獲取上下文內(nèi)部BeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 第三步:對BeanFactory做預備工作
			prepareBeanFactory(beanFactory);

			try {
				// 第四步:允許在上下文子類中對bean工廠進行post-processing
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// 第五步:調(diào)用上下文中注冊為bean的工廠 BeanFactoryPostProcessor
				invokeBeanFactoryPostProcessors(beanFactory);

				// 第六步:注冊攔截bean創(chuàng)建的攔截器
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				// 第七步:初始化MessageSource(國際化相關)
				initMessageSource();

				// 第八步:初始化容器事件廣播器(用來發(fā)布事件)
				initApplicationEventMulticaster();

				// 第九步:初始化一些特殊的bean
				onRefresh();

				// 第十步:將所有監(jiān)聽器注冊到前兩步創(chuàng)建的事件廣播器中
				registerListeners();

				// 第十一步:結束bean的初始化工作(主要將所有單例BeanDefinition實例化)
				finishBeanFactoryInitialization(beanFactory);

				// 第十二步:afterRefresh(上下文刷新完畢,發(fā)布相應事件)
				finishRefresh();
			} catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			} finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}

這里有非常多的步驟,上下文對象主要的bean也是在這里進行處理的,具體的說明可以看注釋, fresh方法就是SpringFrameWork的那部分(不再細化)

//6、ConfigurableApplicationContext根據(jù)webApp…Type進行構造的上下文對象
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

            //7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執(zhí)行所有的initializers邏輯,做初始化準備操作。
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

            //8、refreshContext,可以理解成容器初始化節(jié)點,將執(zhí)行bean的創(chuàng)建和實例化。
            this.refreshContext(context);

     =====> //9、afterRefresh,容器后處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現(xiàn)類并執(zhí)行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啟動最后。
            this.afterRefresh(context, applicationArguments);
     =====> //10、然后根據(jù)stopwatch打印出啟動時間
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
             
            //11、這里調(diào)用ApplicationRunner和CommandLineRunner的實現(xiàn)類
            this.callRunners(context, applicationArguments);

afterRefresh() 是對方法 refresh() 的擴展,暫時空方法。

stopWatch.stop() 根據(jù)stopwatch打印出啟動時間,至此項目已經(jīng)啟動完成。

* run方法主要做如下幾件事情:

  1. 發(fā)出啟動結束事件

  2. 執(zhí)行實現(xiàn)ApplicationRunner、CommandLineRunner的run方法

  3. 發(fā)布應用程序已啟動(ApplicationStartedEvent)事件

結合網(wǎng)上共享的兩張圖可以清晰回顧下整體流程:

Springboot Code的啟動源碼是怎樣的

Springboot Code的啟動源碼是怎樣的

關于Springboot Code的啟動源碼是怎樣的問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業(yè)資訊頻道了解更多相關知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI