中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

Springboot Code的啟動源碼是怎樣的

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

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

項目啟動的流程:

(一)new SpringApplication

  1. 配置source和web環境;

  2. 創建初始化構造器和應用監聽器;

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

(二)run 第一二部分

1.獲取并啟動監聽器

  • 初始化計時stopWatch、啟動上下文bootstrapContext、設置系統參數headless;

  • 初始化監聽器列表SpringApplicationRunListeners;

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

2.準備環境

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

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

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模式,這種模式是一種系統缺少顯示設備、鍵盤和鼠標外設的情況模式。
        this.configureHeadlessProperty();

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

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

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

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

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

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

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

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

            listeners.started(context);
             
            //11、這里調用ApplicationRunner和CommandLineRunner的實現類
            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的創建與準備

    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)創建 AnnotationConfigServletWebServerApplicationContext 對象,對象創建過程中,初始化了父類的屬性值,其中有三個比較關鍵的值,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根據webApp…Type進行構造的上下文對象
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

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

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

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

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

            listeners.started(context);
             
            //11、這里調用ApplicationRunner和CommandLineRunner的實現類
            this.callRunners(context, applicationArguments);
	/**
	 * Spring容器準備
	 */
	private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
								ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
								ApplicationArguments applicationArguments, Banner printedBanner) {
		// 設置上下文環境
		context.setEnvironment(environment);
		//
		postProcessApplicationContext(context);
		// 執行所有ApplicationContextInitializer對象的initialize方法(這些對象是通過讀取spring.factories加載)
		applyInitializers(context);
		// 發布上下文準備完成事件到所有監聽器
		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]));
		// 發送上下文加載完成事件
		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());
        }

    }

    //調用全部的構造器
    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根據webApp…Type進行構造的上下文對象
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

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

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

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

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

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

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

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

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

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

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

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

Springboot Code的啟動源碼是怎樣的

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

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

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

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

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

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

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

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

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

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

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

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

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

				// 第十二步:afterRefresh(上下文刷新完畢,發布相應事件)
				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根據webApp…Type進行構造的上下文對象
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

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

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

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

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

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

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

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

  1. 發出啟動結束事件

  2. 執行實現ApplicationRunner、CommandLineRunner的run方法

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

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

Springboot Code的啟動源碼是怎樣的

Springboot Code的啟動源碼是怎樣的

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

向AI問一下細節

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

AI

滦南县| 赤峰市| 任丘市| 安宁市| 嘉祥县| 泰和县| 伊宁市| 栾川县| 治多县| 庐江县| 渭南市| 赣榆县| 百色市| 游戏| 论坛| 建昌县| 泰兴市| 内黄县| 安阳市| 金溪县| 浙江省| 赤峰市| 宣化县| 抚宁县| 容城县| 兴文县| 徐汇区| 津南区| 郧西县| 无棣县| 吴忠市| 宿州市| 鲁甸县| 鞍山市| 吐鲁番市| 虞城县| 玉屏| 淮阳县| 夹江县| 磐安县| 祁东县|