1. 程式人生 > >32 SpringBoot啟動原理與流程

32 SpringBoot啟動原理與流程

1 啟動原理

1.1 入口(Springboot27Application.java)

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

1.2 開始啟動(SpringBootApplication.java)

public static ConfigurableApplicationContext run
(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { /* 專案啟動分為2步 1: new SpringApplication(primarySources) 建立SpringApplication物件 2: .run(args) 執行run方法 */
return (new SpringApplication(primarySources)).run(args); }
1.2.1 建立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.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); // 1 獲取應用型別 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer,然後儲存起來 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 3 從類路徑下找到META-INF/spring.factories配置的所有ApplicationListener this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); // 4 從多個配置類中找到有main方法的主配置類 this.mainApplicationClass = this.deduceMainApplicationClass(); }
1.2.1.1 獲取應用型別
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;
	}
}
1.2.1.2 配置的所有ApplicationContextInitializer
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = this.getClassLoader();
	Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	......
	return instances;
}
  • SpringFactoriesLoader.loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	String factoryClassName = factoryClass.getName();
	return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
	if (result != null) {
		return result;
	} else {
		try {
			// 從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer
			// eg: org/springframework/boot/spring-boot-autoconfigure/2.1.0.RELEASE/spring-boot-autoconfigure-2.1.0.RELEASE.jar!/META-INF/spring.factories
			# Application Context Initializers
			org.springframework.context.ApplicationContextInitializer=\
			org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
			org.springframework.boot.context.ContextIdApplicationContextInitializer,\
			org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
			org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
			
			/*
				從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer
				
				eg: org/springframework/boot/spring-boot-autoconfigure/2.1.0.RELEASE/spring-boot-autoconfigure-2.1.0.RELEASE.jar!/META-INF/spring.factories
					# Application Context Initializers
					org.springframework.context.ApplicationContextInitializer=\
					org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
					org.springframework.boot.context.ContextIdApplicationContextInitializer,\
					org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
					org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
			*/
			Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
			......
			// 儲存起來
			cache.put(classLoader, result);
			return result;
		} catch (IOException var13) {
			throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
		}
	}
}

所有的ApplicationContextInitializer

1.2.1.3 配置的所有ApplicationListener
原理與 1.2.1.2 中一致

所有ApplicationListener

1.2.1.4 配置的所有ApplicationListener
1.2.2 執行run方法
public ConfigurableApplicationContext run(String... args) {
	// 1 監聽
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	
	// 2 宣告一個空的IOC容器
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
	this.configureHeadlessProperty();
	
	// 3 獲取SpringApplicationRunListeners: 底層使用getSpringFactoriesInstances()方法獲取,原理與1.2.1.2中一致(從類路徑下META-INF/spring.factories)
	SpringApplicationRunListeners listeners = this.getRunListeners(args);
	
	// 4 回撥所有的獲取SpringApplicationRunListener.starting()方法
	listeners.starting();

	Collection exceptionReporters;
	try {
		// 5 封裝命令列引數(專案啟動時,可以在命令列中指定引數)
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		
		//6 準備環境,建立環境完成後回撥SpringApplicationRunListener.environmentPrepared();表示環境準備完成
		ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
		
		this.configureIgnoreBeanInfo(environment);
		
		// 7 列印banner圖示
		Banner printedBanner = this.printBanner(environment);
		
		// 8 建立ApplicationContext : 建立IOC容器
		context = this.createApplicationContext();
		exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
		
		/*
			9 準備上下文環境 
				將配置好的環境放入IOC容器
				
				applyInitializers():回撥之前儲存的所有的ApplicationContextInitializer的initialize方法(1.2.1.2)
				
				回撥所有的SpringApplicationRunListener的contextPrepared();
				
				prepareContext執行完成以後回撥所有的SpringApplicationRunListener的contextLoaded()
		*/
		this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		
	
        // 10 重新整理容器 : ioc容器初始化(如果是web應用還會建立嵌入式的Tomcat)
		// 掃描,建立,載入所有元件的地方;(配置類,元件,自動配置)
		this.refreshContext(context);
		
		this.afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
		}

		listeners.started(context);
		
		/*
			11 從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進行回撥
				ApplicationRunner先回調,CommandLineRunner再回調
		*/
		this.callRunners(context, applicationArguments);
	} catch (Throwable var10) {
		this.handleRunFailure(context, var10, exceptionReporters, listeners);
		throw new IllegalStateException(var10);
	}

	try {
		listeners.running(context);
		
		//整個SpringBoot應用啟動完成以後返回啟動的ioc容器;
		return context;
	} catch (Throwable var9) {
		this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
		throw new IllegalStateException(var9);
	}
}
1.2.2.1 獲取SpringApplicationRunListeners

3 獲取SpringApplicationRunListeners

1.2.2.2 準備環境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
	// 判斷環境是否存在,存在及獲取,不存在及建立
	ConfigurableEnvironment environment = this.getOrCreateEnvironment();
	
	// 配置環境
	this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
	
	// 建立環境完成後回撥SpringApplicationRunListener.environmentPrepared();表示環境準備完成
	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;
}
1.2.2.3 準備上下文環境
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	// 將配置好的環境放入IOC容器
	context.setEnvironment(environment);
	
	// 後置處理
	this.postProcessApplicationContext(context);
	
	// 回撥之前儲存的所有的ApplicationContextInitializer的initialize方法
	this.applyInitializers(context);
	
	// 回撥所有的SpringApplicationRunListener的contextPrepared(1.2.1.3)
	listeners.contextPrepared(context);
	
	// 日誌記錄
	if (this.logStartupInfo) {
		this.logStartupInfo(context.getParent() == null);
		this.logStartupProfileInfo(context);
	}

	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	
	// 將命令列引數註冊進來
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	
	// 將banner註冊
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}

	if (beanFactory instanceof DefaultListableBeanFactory) {
		((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}

	Set<Object> sources = this.getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	this.load(context, sources.toArray(new Object[0]));
	
	// 回撥所有的SpringApplicationRunListener的contextLoaded()
	listeners.contextLoaded(context);
}