以下代码为SpringBoot应用的启动类的代码,相信每个人都见过太多次这个启动类了,main方法中只有一行代码,这行代码到底发生了什么?
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
Spring应用的启动
从字面意义上来看其实就是Spring应用run起来,我们知道一个Spring应用的启动需要做哪些?
- 读取配置信息
- 创建合适的上下文容器并读取bean定义
- 实例化单例非lazy加载的bean实例
- 其他功能填充
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();
}
- 将传入的配置类保存在自己私有的配置类Set中保存
- 推断当前是否为web环境
- 从SpringFactories文件中读取
ApplicationContextInitializer
的实现类 - 从SpringFactories文件中读取
ApplicationListener
的实现类 - 推断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返回
为什么要排序?
既然是同一个接口的多个实现,那么在调用的时候肯定有个顺序,因此在这里获取到实例后根据排序规则先排序好,之后顺序调用即可
在这里获取的是ApplicationContextInitializer
和ApplicationListener
的实现类,后者为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);
}
}
- 创建用于计算启动时间的StopWatch
- 设置
java.awt.headless
系统属性 - 拿到所有监听器并调用监听器的
starting
方法 - 封装参数为
ApplicationArguments
- 准备一些环境信息,会调用监听器的
environmentPrepared
方法 - 打印banner
- 创建合适的上下文容器(web上下文 :标准上下文)
- 创建错误分析器
- 准备上下文环境
- 刷新上下文环境
- 调用刷新完成的回调函数
- 调用监听器中的finished函数
- 打印启动时间并返回上下文容器
- 异常处理
整个过程清晰明了,就是准备好一个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;
}
- 获取或创建一个environment
- 配置环境
- 调用监听器的
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);
}
- 将环境信息塞入context中
- 调用
postProcessApplicationContext
,子类可覆盖实现个性化,这里主要是对beanName生成器、资源加载器和类加载器进行设置 - 调用之前拿到的
ApplicationContextInitializer
- 调用监听器的
contextPrepared
方法 - 注册一些特定的单例bean
- 加载beans资源信息
- 调用监听器的
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);
}
}
很简单,就是对ApplicationRunner
和CommandLineRunner
进行调用,而且从代码中可以看到,将这两个接口的实现类添加进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方法后对错误进行分析并进行日志的打印,关闭容器