SpringApplication.run到底发生了什么?

以下代码为SpringBoot应用的启动类的代码,相信每个人都见过太多次这个启动类了,main方法中只有一行代码,这行代码到底发生了什么?

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

Spring应用的启动

从字面意义上来看其实就是Spring应用run起来,我们知道一个Spring应用的启动需要做哪些?

  1. 读取配置信息
  2. 创建合适的上下文容器并读取bean定义
  3. 实例化单例非lazy加载的bean实例
  4. 其他功能填充

SpringBoot将这些过程帮我们打包一起,通过一行代码SpringApplication.run来一键启动我们的Spring应用

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

我们追踪run方法的调用链发现其初始化了一个SpringApplication对象并调用了其run方法。而source为我们传入其中的带有@SpringBootApplication注解的类,以及自定义参数args。
我们注意到存在重载方法

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

即我们可以传入配置类数组来进行启动Spring应用

SpringApplication对象的初始化

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();
		//读取ApplicationContextInitializer
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		//读取ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//推断主函数
		this.mainApplicationClass = deduceMainApplicationClass();
	}
  1. 将传入的配置类保存在自己私有的配置类Set中保存
  2. 推断当前是否为web环境
  3. 从SpringFactories文件中读取ApplicationContextInitializer的实现类
  4. 从SpringFactories文件中读取ApplicationListener的实现类
  5. 推断main函数类
推断是否为web环境
private boolean deduceWebEnvironment() {
		for (String className : WEB_ENVIRONMENT_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return false;
			}
		}
		return true;
	}
public static boolean isPresent(String className, ClassLoader classLoader) {
        try {
            forName(className, classLoader);
            return true;
        } catch (Throwable var3) {
            return false;
        }
    }

由代码可知是通过反射从ClassLoader中寻找是否已经加载了某个类,Spring将检测是否存在"javax.servlet.Servlet""org.springframework.web.context.ConfigurableWebApplicationContext",如果两个都存在则为web环境,否则为一般环境

从SpringFactories中加载自动装配的类

我们知道SpringBoot的starter用起来相当方便,在org/springframework/boot/spring-boot/1.5.6.RELEASE/spring-boot-1.5.6.RELEASE.jar!/META-INF/spring.factories中定义了一些借口的实现类,SpringBoot会帮我们自动引入而无需我们手动操作

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<String>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

通过方法getSpringFactoriesInstances来实现Spring.factories的读取和实现类的加载。
SpringFactoriesLoader.loadFactoryNames(type, classLoader));会通过classLoader加载资源文件Spring.factories,并根据传入的type类型拿到对应的所有的实现类的类型列表,并通过createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);进行实例化

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<?> instanceClass = ClassUtils.forName(name, classLoader);
				Assert.isAssignable(type, instanceClass);
				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;
	}

方法很简单,根据类名拿到Class对象,然后拿到无参构造器(因为方法调用的实际传递的parameterTypes为一个空的数组),使用构造器进行实例的初始化操作。
我们注意到在实例初始化完成后,还根据实现类的的@Order进行了一次排序,并将有序的实例List返回
为什么要排序?
既然是同一个接口的多个实现,那么在调用的时候肯定有个顺序,因此在这里获取到实例后根据排序规则先排序好,之后顺序调用即可

在这里获取的是ApplicationContextInitializerApplicationListener的实现类,后者为Spring应用期间的一系列事件监听器

推断主函数类
private Class<?> deduceMainApplicationClass() {
		try {
			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;
	}

很秒的实现,我们知道在出现异常打印异常栈信息的时候会发现,栈底的方法就是main方法,因此这里直接new了一个运行时异常并获取它的异常栈信息,通过遍历栈内方法名为main的,拿到主函数对应的类

为什么要推断主函数类?

run

接下来就是run方法了

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			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);
		}
	}

  1. 创建用于计算启动时间的StopWatch
  2. 设置java.awt.headless系统属性
  3. 拿到所有监听器并调用监听器的starting方法
  4. 封装参数为ApplicationArguments
  5. 准备一些环境信息,会调用监听器的environmentPrepared方法
  6. 打印banner
  7. 创建合适的上下文容器(web上下文 :标准上下文)
  8. 创建错误分析器
  9. 准备上下文环境
  10. 刷新上下文环境
  11. 调用刷新完成的回调函数
  12. 调用监听器中的finished函数
  13. 打印启动时间并返回上下文容器
  14. 异常处理

整个过程清晰明了,就是准备好一个Spring容器,并在合适的时间调用监听器的相应函数,下面进行详细讲解

java.awt.headless

有些功能函数是需要显示屏、键盘鼠标等设备支持的,而在服务器下可能不存在这些硬件,因此使用该系统设置能够使得这些函数可以被正常使用

prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		if (!this.webEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		return environment;
	}
  1. 获取或创建一个environment
  2. 配置环境
  3. 调用监听器的environmentPrepared方法
private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		if (this.webEnvironment) {
			return new StandardServletEnvironment();
		}
		return new StandardEnvironment();
	}

可以看到根据我们初始化SpringApplication的时候推断出的是否为web环境信息来创建Servlet环境或者标准环境
拿到环境后就要进行配置了,配置什么?还记得我们传入的args吗,其实就是将这些args塞进去

protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}
创建Spring容器
protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				contextClass = Class.forName(this.webEnvironment
						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
	}

根据是否为web容器来创建不同的ApplicationContext

准备上下文环境prepareContext
private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		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 = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[sources.size()]));
		listeners.contextLoaded(context);
	}
  1. 将环境信息塞入context中
  2. 调用postProcessApplicationContext,子类可覆盖实现个性化,这里主要是对beanName生成器、资源加载器和类加载器进行设置
  3. 调用之前拿到的ApplicationContextInitializer
  4. 调用监听器的contextPrepared方法
  5. 注册一些特定的单例bean
  6. 加载beans资源信息
  7. 调用监听器的contextLoaded方法
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(
					AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					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());
			}
		}
	}
protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug(
					"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		BeanDefinitionLoader loader = createBeanDefinitionLoader(
				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();
	}

在load方法中,创建了BeanDefinitionLoader对象用来加载BeanDefinition,并对其的一些配置信息进行完善

public int load() {
		int count = 0;
		for (Object source : this.sources) {
			count += load(source);
		}
		return count;
	}
protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug(
					"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		BeanDefinitionLoader loader = createBeanDefinitionLoader(
				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();
	}

注意这里是对source进行的BeanDefinition注册,即对我们启动Spring应用时传入的配置类进行Bean定义的注册

refresh!!!

是不是很熟悉?没错,就是我们熟悉的那个AbstractApplicationContext中的refresh方法,这里完成的Spring容器接下来的所有的初始化功能,不在这里细讲了

afterRefresh
protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
		callRunners(context, args);
	}

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<Object>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<Object>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

	private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args);
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
		}
	}

	private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args.getSourceArgs());
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
		}
	}

很简单,就是对ApplicationRunnerCommandLineRunner进行调用,而且从代码中可以看到,将这两个接口的实现类添加进runners中时是先添加的ApplicationRunner再添加的CommandLineRunner,然后根据@Order排序后进行遍历调用,如果没有实现自定义顺序,所有的ApplicationRunner优先于CommandLineRunner被调用

handleRunFailure

当run的过程中发生异常时,会调用该方法进行异常处理

private void handleRunFailure(ConfigurableApplicationContext context,
			SpringApplicationRunListeners listeners, FailureAnalyzers analyzers,
			Throwable exception) {
		try {
			try {
				handleExitCode(context, exception);
				listeners.finished(context, exception);
			}
			finally {
				reportFailure(analyzers, exception);
				if (context != null) {
					context.close();
				}
			}
		}
		catch (Exception ex) {
			logger.warn("Unable to close ApplicationContext", ex);
		}
		ReflectionUtils.rethrowRuntimeException(exception);
	}
	
private void handleExitCode(ConfigurableApplicationContext context,
			Throwable exception) {
		int exitCode = getExitCodeFromException(context, exception);
		if (exitCode != 0) {
			if (context != null) {
				context.publishEvent(new ExitCodeEvent(context, exitCode));
			}
			SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
			if (handler != null) {
				handler.registerExitCode(exitCode);
			}
		}
	}

根据异常拿到对应的exitCode并发布事件通知事件监听器,同时拿到该异常的异常处理器注册exitCode;之后调用监听器的finished方法后对错误进行分析并进行日志的打印,关闭容器

发布了98 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Mutou_ren/article/details/104071485