SpringBoot源码-启动

SpringBoot源码-启动
源码解析 SpringBoot 的启动很简单,一行代码就能完成:

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

但在这简单的代码背后,SpringBoot帮助我们完成了事件的注册,容器的生成。今天追踪源码,看一下SpringBoot的启动背后,究竟做了哪些事情。
SpringApplication.run()的执行会首先新建一个SpringApplication的实例,然后调用实例的run函数。
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); }

新建SpringApplication实例,会调用initialize方法。
public SpringApplication(Object... sources) { initialize(sources); }private void initialize(Object[] sources) { // 将source加载近实例的sources属性中 if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } // bool类型,用来标识当前的环境是否为web环境 this.webEnvironment = deduceWebEnvironment(); // 将spring.factories中的ApplicationContextInitializer加载入实例的属性中 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 将spring.factories中的ApplicationListener加载入实例的属性中 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // trace 递归找出main函数所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }

其中:
  1. deduceWebEnvironment函数用来判断当前的Application是否为web环境,判断的方法是去尝试实例化Servlet,ConfigurableWebApplicationContext,如果成功,则认为是,反之则为否。
  2. setInitializerssetListeners是通过扫描spring.factories文件来完成的。该文件的位置为jar包的resource/META-INF文件夹下,需要注意的是,文件可以存在于多个jar包中,这不影响springboot启动时的扫描,所以如果需要自定义启动类和监听类,只需要在自己的jar包中添加文件即可。
【SpringBoot源码-启动】SpringApplication的实例化完成后,就是执行run方法:
public ConfigurableApplicationContext run(String... args) { // 构建了一个任务执行的观察器,通过start和stop可以完成任务的计时 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); // 获取SpringApplicationRunListeners,内部只有一个EventPublishingRunListener SpringApplicationRunListeners listeners = getRunListeners(args); // 通过EventPublishingRunListener向所有注册的listener类发送ApplicationStartedEvent事件 listeners.started(); try { // 构造一个应用程序参数持有类 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 创建spring容器 context = createAndRefreshContext(listeners, applicationArguments); // spring容器创建完成后的回调任务 afterRefresh(context, applicationArguments); // 通过EventPublishingRunListener向所有注册的listener类发送getFinishedEvent事件 listeners.finished(context, null); // stop记录point stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } // 返回ConfigurableApplicationContext return context; } catch (Throwable ex) { handleRunFailure(context, listeners, ex); throw new IllegalStateException(ex); } }

首先来说明一下SpringApplicationRunListeners,这个类在SpringBoot中只有一个实现:EventPublishingRunListener。该类的ApplicationEventMulticaster成员,其实充当了一个送报员的角色,在收到了报纸(主线程的通知)后,将根据订阅的人员名单(在Application中注册的Listener列表),进行事件的分发通知。执行的步骤如图:
SpringBoot源码-启动
文章图片
@SpringApplicationRunListener执行步骤|center 具体的代码简略如下:
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {private final ApplicationEventMulticaster multicaster; ……@Override public void finished(ConfigurableApplicationContext context, Throwable exception) { publishEvent(getFinishedEvent(context, exception)); }……// 事件的分发 private void publishEvent(SpringApplicationEvent event) { this.multicaster.multicastEvent(event); }}

这样就很容易理解run函数中的listener.start()listener.stop()的作用了,用来标记启动的状态并通知各个注册的listener作出相应的操作。
createAndRefreshContext 是创建Spring容器的过程:
private ConfigurableApplicationContext createAndRefreshContext( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 定义spring容器 context ConfigurableApplicationContext context; // 根据之前的webEnvironment来初始化相应的环境 //(StandardEnvironment或者StandardServletEnvironment) ConfigurableEnvironment environment = getOrCreateEnvironment(); // 配置环境信息 configureEnvironment(environment, applicationArguments.getSourceArgs()); // 通过EventPublishingRunListener向所有注册的listener类 // 发送ApplicationEnvironmentPreparedEvent事件 listeners.environmentPrepared(environment); // 重新校验环境信息 if (isWebEnvironment(environment) && !this.webEnvironment) { environment = convertToStandardEnvironment(environment); }// print 环境信息 if (this.bannerMode != Banner.Mode.OFF) { printBanner(environment); }// Create, load, refresh and run the ApplicationContext // 创建Spring容器 context = createApplicationContext(); // 配置Spring容器 context.setEnvironment(environment); // Spring容器创建之后需要做的回调方法 postProcessApplicationContext(context); // 执行 SpringApplication 的初始化器 applyInitializers(context); // 遍历调用SpringApplicationRunListener的contextPrepared方法 // 查看代码来看,只是将ApplicationEventMulticaster注册到Spring容器中 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); }// Add boot specific singleton beans // 把应用程序参数持有类注册到Spring容器中,并且是一个单例 context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); // Load the sources Set sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[sources.size()])); // 通过EventPublishingRunListener广播发送ApplicationPreparedEvent给所有注册的listener类 listeners.contextLoaded(context); // Refresh the context // 刷新Spring容器 refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } return context; }
Spring容器的创建:
protected ConfigurableApplicationContext createApplicationContext() { Class contextClass = this.applicationContextClass; if (contextClass == null) { try { // 如果是web程序,那么构造AnnotationConfigEmbeddedWebApplicationContext容器 // 否则构造AnnotationConfigApplicationContext容器 // 这里也是利用了初始化SpringApplication的webEnvironment变量 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); }

Spring容器创建之后的后续操作:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.webEnvironment) { // web环境 && Spring环境 注册实例命名生成器(后续章节详细讲) if (context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext configurableContext = (ConfigurableWebApplicationContext) context; if (this.beanNameGenerator != null) { configurableContext.getBeanFactory().registerSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } } }// 设置资源loader if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context) .setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context) .setClassLoader(this.resourceLoader.getClassLoader()); } } }

applyInitialzers,遍历初始化时加载的Initialzer类,并执行initialize方法。
比如ContextIdApplicationContextInitializer会设置应用程序的id;AutoConfigurationReportLoggingInitializer会给应用程序添加一个条件注解解析器报告等:
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }

Spring容器的refresh方法中会执行很多事情:例如BeanFactory的设置,BeanFactoryPostProcessor接口的执行、BeanPostProcessor接口的执行、自动化配置类的解析、条件注解的解析、国际化的初始化等等。后续详细说明。
run方法中在执行完毕createAndRefreshContext后,还执行了afterRefresh函数
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { // 已弃用 afterRefresh(context, args.getSourceArgs()); // 加载runner callRunners(context, args); }private void callRunners(ApplicationContext context, ApplicationArguments args) { List runners = new ArrayList(); // 找出Spring容器中ApplicationRunner接口的实现类 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // 找出Spring容器中CommandLineRunner接口的实现类 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); // 排序 AnnotationAwareOrderComparator.sort(runners); // 遍历执行run for (Object runner : new LinkedHashSet(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
自此而止,整个SpringBoot的启动就已经完成了,各种监听器,各种初始化器,各种Runner也已经完成了对于的工作
总结 SpringBoot在启动的过程中主要分为两个部分:SpringApplication的初始化实例的run方法:
1. SpringApplication的初始化
  1. 设置source到实例的属性
  2. 检验web环境并标识
  3. 找出需要处理的所有initializerslistener,设置到实例的属性中
  4. 找出main函数所在的类
2. 实例的run方法
  • 1.设置事件分发器,充当送报员的角色
  • 2.构造Spring容器
    • 2.1 创建Spring容器
    • 2.2 遍历执行initializersinitialize方法
    • 2.3 Spring容器的刷新(完成bean的解析、各种processor接口的执行、条件注解的解析等)
  • 3.从Spring容器中找出ApplicationRunnerCommandLineRunner接口的实现类并排序后依次执行

    推荐阅读