SpringBoot2啟動流程分析


首先上一張圖,圖片來自 SpringBoot啟動流程解析

SpringBoot啟動結構圖.png

本文基於spring-boot-2.0.4.RELEASE.jar包分析。

程序啟動入口

public static void main(String[] args) {
	SpringApplication.run(Springboot2Application.class, args);
}

run是一個靜態方法,最后會調用創建SpringApplication實例並run

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class[]{primarySource}, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return (new SpringApplication(primarySources)).run(args);
}

我們先看new SpringApplication(xx)構建的實例。

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.headless = true;
	this.registerShutdownHook = true;
	this.additionalProfiles = new HashSet();
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	//配置source
	this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
	//判斷是否web應用
	this.webApplicationType = this.deduceWebApplicationType();
	//創建初始化構造器
	this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
	//創建應用監聽器
	this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
	//推斷出main方法類
	this.mainApplicationClass = this.deduceMainApplicationClass();
}

SpringApplication的構造函數主要設置了一些基本參數並配置source、判斷了是否web應用、創建初始化構造器、創建應用監聽器、找出main方法所在的類。
getSpringFactoriesInstances為獲取spring工廠中的實例,具體代碼塊為

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
	return this.getSpringFactoriesInstances(type, new Class[0]);
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    //獲取類加載器
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	//通過類加載器、獲取指定的spring.factories文件,並獲取文件中工廠類的全路徑名
	Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //通過類路徑反射得到工廠class對象、構造方法
	List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

new SpringApplication(xx)構建完實例后,會進行run方法。

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
        //應用啟動計時器開始計時
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //Headless模式配置
		configureHeadlessProperty();
        //獲取啟動監聽器
		SpringApplicationRunListeners listeners = getRunListeners(args);
        //應用啟動監聽器開始計時
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
             //配置環境模塊(祥見下面prepareEnvironment方法)
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
             //配置需要忽略的bean
			configureIgnoreBeanInfo(environment);
            //Banner配置
			Banner printedBanner = printBanner(environment);
            //創建應用上下文對象
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            //上下文基本屬性配置(祥見下面prepareContext方法)
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            //更新應用上下文(祥見下面refresh方法)
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 創建配置環境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
        //加載屬性文件
		configureEnvironment(environment, applicationArguments.getSourceArgs());
        //監聽器監聽配置
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
        //加載配置環境
		context.setEnvironment(environment);
        //ResourceLoader資源加載器
		postProcessApplicationContext(context);
        //初始化環境
		applyInitializers(context);
        //配置監聽
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
        //加載啟動參數
		load(context, sources.toArray(new Object[0]));
        //監聽加載應用上下文
		listeners.contextLoaded(context);
	}
 public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            //准備更新上下文時的預備工作:1.初始化PropertySource; 2.驗證Enrivonment中必要的屬性
            this.prepareRefresh();
            //獲取上下文中的BeanFactory
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //對BeanFactory做些預備工作
            this.prepareBeanFactory(beanFactory);

            try {
                //對BeanFactory進行預處理
                this.postProcessBeanFactory(beanFactory);
                //執行容器中的BeanFactoryPostProcessor
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //注冊BeanPostProcessor
                this.registerBeanPostProcessors(beanFactory);
                //初始化MessageSource(國際化相關)
                this.initMessageSource();
                //初始化容器事件廣播器(用來發布事件)
                this.initApplicationEventMulticaster();
                //初始化一些特殊的Bean,主要做了: 1.初始化ThemeSource(跟國際化相關的接口) 2.創建WebServer
                this.onRefresh();
                //將所有監聽器注冊到前兩步創建的事件廣播器中
                this.registerListeners();
                //結束BeanFactory的初始化工作(這一步主要用來將所有的單例BeanDefinition實例化)
                this.finishBeanFactoryInitialization(beanFactory);
                //上下文刷新完畢
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }


免責聲明!

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



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