NOTE

2.63 启动原理

1. 启动SpringBoot 我们直接从SpringBoot启动的这行代码开始 忽略简单的嵌套调用,最终调用的代码如下: 首先会使用主配置类Application.class创建SpringApplication,然后传入启动参数args启动 2. 创建SpringApplication的过程 我

Java创建于 更新于 historical

这是历史学习笔记,可能存在过时或不完整的理解。

1. 启动SpringBoot

我们直接从SpringBoot启动的这行代码开始

SpringApplication.run(Application.class, args)

忽略简单的嵌套调用,最终调用的代码如下:

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
	//(Application.class, args)
	return new SpringApplication(sources).run(args);
}

首先会使用主配置类Application.class创建SpringApplication,然后传入启动参数args启动

2. 创建SpringApplication的过程

我们首先看SpringApplicaiton是怎么创建的

public SpringApplication(Object... sources) {
	//创建的时候初始化
	initialize(sources);
}

private void initialize(Object[] sources) {
	//保存配置类
	if (sources != null && sources.length > 0) {
		this.sources.addAll(Arrays.asList(sources));
	}
	//判断是否web环境并保存
	this.webEnvironment = deduceWebEnvironment();
	//在类路径下META-INF/spring.factories找到所有的ApplicationContextInitializer类,保存起来
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	//在类路径下META-INF/spring.factories找到所有的ApplicationListener类,保存起来
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	//判断主配置类(有main方法的)并保存
	this.mainApplicationClass = deduceMainApplicationClass();
}

初始化主要分为5个步骤

2.1. 判断是否web环境

private boolean deduceWebEnvironment() {
	//private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
	//		"org.springframework.web.context.ConfigurableWebApplicationContext" }
	//如果能加载以上任意一个就是web环境
	for (String className : WEB_ENVIRONMENT_CLASSES) {
		//加载失败返回false
		if (!ClassUtils.isPresent(className, null)) {
			return false;
		}
	}
	return true;
}

2.2. 加载所有的ApplicationContextInitializer

2.2.1. 首先找到所有的ApplicationContextInitializer类,分为三个步骤

  • 找到所有的ApplicationContextInitializer类的名字
  • 实例化
  • 排序
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
		Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	//找到所有的ApplicationContextInitializer类的名字
	Set<String> names = new LinkedHashSet<String>(
			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	//实例化
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
			classLoader, args, names);
	//排序
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}
2.2.1.1. 那么是怎么找到名字的呢
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
	String factoryClassName = factoryClass.getName();
	try {
		//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
		//使用当前线程的类加载器加载类路径resources目录下的所有META-INF/spring.factories文件
		Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
		List<String> result = new ArrayList<String>();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			//每个文件解析成Properties类实例
			Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
			//找到名字为org.springframework.context.ApplicationContextInitializer的属性对应的value
			String propertyValue = properties.getProperty(factoryClassName);
			//分割保存到list中返回
			for (String factoryName : StringUtils.commaDelimitedListToStringArray(propertyValue)) {
				result.add(factoryName.trim());
			}
		}
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}
2.2.1.2. 创建实例的操作其实就是通过反射调用构造方法
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
		Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
		Set<String> names) {
	List<T> instances = new ArrayList<T>(names.size());
	for (String name : names) {
		try {
			//通过类名和类加载器创建class实例
			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
			Assert.isAssignable(type, instanceClass);
			//获取class的构造方法
			Constructor<?> constructor = instanceClass
					.getDeclaredConstructor(parameterTypes);
			//通过构造方法和参数实例化
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
			instances.add(instance);
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException(
					"Cannot instantiate " + type + " : " + name, ex);
		}
	}
	return instances;
}

2.2然后保存到initializers中

public void setInitializers(
		Collection<? extends ApplicationContextInitializer<?>> initializers) {
	this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
	this.initializers.addAll(initializers);
}

2.3. 加载所有的ApplicationListener

逻辑同2

2.4. 保存主配置类

private Class<?> deduceMainApplicationClass() {
	try {
		//遍历调用栈的方法名,有main的就是主配置类
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			if ("main".equals(stackTraceElement.getMethodName())) {
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

2.5. 运行SpringBoot Application

public ConfigurableApplicationContext run(String... args) {
	//创建计时器,并启动
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	FailureAnalyzers analyzers = null;
	configureHeadlessProperty();
	//加载类路径下所有resources目录下的META-INF/spring.factories文件
	//创建名字为org.springframework.boot.SpringApplicationRunListener的bean,保存到listeners
	SpringApplicationRunListeners listeners = getRunListeners(args);
	//调用所有SpringApplicationRunListener的starting方法
	//其实就是通过广播器广播时间,最后回调listener的onApplicationEvent方法
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
		//创建环境bean
		//web的话创建StandardServletEnvironment,否则StandardEnvironment
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
		//打印banner
		//如果关闭了那就不打印
		//设置了log模式,使用logger打印
		//默认console模式,使用System.out输出到控制台
			//加载image。banner.image.location属性,没有的话resouces目录下的banner.gif等
			//加载text。banner.location属性。
			//以上没有的话使用默认的SpringBootBanner
		//调用banner的printBanner方法
			//打印字符串
			//打印版本信息
		Banner printedBanner = printBanner(environment);
		//创建applicaiton context
		//web的话创建AnnotationConfigEmbeddedWebApplicationContext
		//否则AnnotationConfigApplicationContext
		context = createApplicationContext();
		//创建FailureAnalyzers
		//加载类路径下所有resources目录下的META-INF/spring.factories文件
		//创建所有名字为org.springframework.boot.diagnostics.FailureAnalyzer的bean,保存到analyzers中
		//如果是BeanFactoryAware类型的,那么注入BeanFactory
		analyzers = new FailureAnalyzers(context);
		//准备容器
		//调用所有ApplicationContextInitializer的initialize方法
		//调用所有SpringApplicationRunListener的contextPrepared方法
		//打印启动信息
		//打印profile信息
		//注册主配置类的BeanDefinition
		//调用所有SpringApplicationRunListener的contextLoaded方法
		prepareContext(context, environment, listeners, applicationArguments,
				printedBanner);
		//刷新容器。refresh
		refreshContext(context);
		//完成刷新容器后。
		//调用ApplicationRunner、CommandRunnner的run方法
		//调用SpringApplicationRunListener的finished方法
		afterRefresh(context, applicationArguments);
		listeners.finished(context, null);
		//停止计时器
		stopWatch.stop();
		//打印启动时长
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		return context;
	}
	catch (Throwable ex) {
		handleRunFailure(context, listeners, analyzers, ex);
		throw new IllegalStateException(ex);
	}
}

可以看出调用了spring的refresh方法,参考1.创建spring容器.md