springboot源码(一)启动

352 阅读3分钟

springboot(一)启动流程分析

本文仅此记录阅读springboot2.5.4源码的一些总结分析,并对部分源码做了批注,方便理解springboot的启动流程和spring bean的生命周期

springboot启动分为两个部分,下面我会分别探究

官方文档:docs.spring.io/spring-boot…

我们的springboot项目启动时,通常会包含一个主启动类

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

最终还是调用的内部一个run方法,我们可以主观的把它分为两部分

1 创建 SpringApplication对象

2 执行SpringApplication的run方法


public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
   return run(new Class<?>[] { primarySource }, args);
}
// 也可以直接调用这个方法,传入多个主启动类
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

第一部分

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	// 传入为null
    	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
    	// 主启动类
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   	// 类型推断(会根据使用的类判断)
    	// org.springframework.web.servlet.DispatcherServlet(soringmvc核心入口)这个类
    	// 没设么特殊情况,我们web服务都是返回的	SERVLET	类型
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
   	// 在SpringFactories中寻找并注册注册BootstrapRegistryInitializer(函数式接口)
    	// 这是个回调接口,会在run方法的createBootstrapContext()方法调用
	this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
    	// 同样这也是在SpringFactories寻找并注册ApplicationContextInitializer
    	// 官方的介绍是这样
    	// 用于在刷新之前初始化Spring ConfigurableApplicationContext的回调接口,通常在需要对应用程序上下文进行一些编程初始化的web应用程序中使用
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	// 在SpringFactories寻找并注册ApplicationListener
    	// 这是spring提供的事件监听器,方便对整个springboot的流程进行扩展		
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    	// 推断main方法的类
    	// StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
    	// 获取栈的信息遍历栈所有方法来获取main方法类
	this.mainApplicationClass = deduceMainApplicationClass();
}

下面时springboot默认提供的监听器和初始化回调类

image-20220418124946577.png

image-20220418124517154.png

第二部分

public ConfigurableApplicationContext run(String... args) {
    	// 计时器,记录启动时间等等
	StopWatch stopWatch = new StopWatch();
    	// 开始计时
	stopWatch.start();
    	// 维护了一个上下文环境,用于提供大量参数的管理,也方便统一些事件 的参数
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	ConfigurableApplicationContext context = null;
    	// 配置 java.awt.headless 进系统参数
    	// 		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
	configureHeadlessProperty();
    	// 获取spirng的监听器
	SpringApplicationRunListeners listeners = getRunListeners(args);
    	// 发布容器启动事件
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	try {
            // 获取args参宿和
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 准备环境遍历
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            // 配置忽略BeanInfo
            configureIgnoreBeanInfo(environment);
            // 打印spring的启动banner
            Banner printedBanner = printBanner(environment);
            // 根据推断的web类型启动相应的容器
            // mvc为AnnotationConfigServletWebServerApplicationContext
            // 内嵌了tomcat(重写了容器启动的onRefresh()方法,配置并启动Tomcat对象)
            context = createApplicationContext();
            // 设置一下容器当前状态
            context.setApplicationStartup(this.applicationStartup);
            // 准备容器
            prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            // 刷新容器(spring容器启动,也是核心)
            // 对beanfactory的一系列操作都在里面
            refreshContext(context);
            // 模板方法,啥都没有,用于子类扩展实现
            afterRefresh(context, applicationArguments);
            // 停止计时
            stopWatch.stop();
            // 启动容器启动完成事件
            listeners.started(context);
            // 虽然启动完成了但是spring还是为我们保留了一点扩展口
            // ApplicationRunner和CommandLineRunner的回调
            callRunners(context, applicationArguments);
        }catch (Throwable ex) {
            // 调用启动失败事件
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);}
        try {
            // 发布 运行中事件
            listeners.running(context);
        }catch (Throwable ex) {
            // 调用启动失败事件
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
     }

下面是对run方法的细节的详细分析

创建引导上下文

createBootstrapContext()

private DefaultBootstrapContext createBootstrapContext() {
   // 创建了一个空的DefaultBootstrapContext
   DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
   // 这里就是上文所说的,会在这回调所有BootstrapRegistryInitializer的initialize方法
   // 但是这里spring并没有提供默认BootstrapRegistryInitializer,应该是留给我们程序猿扩展吧
   this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
   return bootstrapContext;
}

获取监听器

getRunListeners(args)

private SpringApplicationRunListeners getRunListeners(String[] args) {
   Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
   // 获取所有默认提供的SpringApplicationRunListener,即SpringApplication运行方法的侦听器
   // 这里是boot提供的
   // 这里springboot就提供了一个监听器 EventPublishingRunListener
   return new SpringApplicationRunListeners(logger,
         getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
         this.applicationStartup);
}

写到这不知道大家有没有晕,这么多listener和listeners

class SpringApplicationRunListeners {

   private final Log log;

   private final List<SpringApplicationRunListener> listeners;

   private final ApplicationStartup applicationStartup;

   void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
      doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
            (step) -> {
               if (mainApplicationClass != null) {
                  step.tag("mainApplicationClass", mainApplicationClass.getName());
               }
            });
   }

     ......
     ......

   private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
      doWithListeners(stepName, listenerAction, null);
   }

   private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
         Consumer<StartupStep> stepAction) {
      StartupStep step = this.applicationStartup.start(stepName);
      this.listeners.forEach(listenerAction);
      if (stepAction != null) {
         stepAction.accept(step);
      }
      step.end();
   }

}

SpringApplicationRunListeners默认的监听器(部分源码)

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

   private final SpringApplication application;
   private final SimpleApplicationEventMulticaster initialMulticaster;

   @Override
   public void starting(ConfigurableBootstrapContext bootstrapContext) {
      this.initialMulticaster
            .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
   }

    ......
}

默认情况下SpringApplicationRunListeners只有一个监听器EventPublishingRunListener,每次SpringApplicationRunListeners发布一个事件,都回统一调用doWithListeners(), 其实就是调用EventPublishingRunListener的不同的事件方法,而EventPublishingRunListener又分装了一下,所有的事件都会调用内部维护的SimpleApplicationEventMulticaster类的multicastEvent()

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
    // 获取所有符合类型的事件,然后依次调用(有点像责任链,同时类型匹配,还使用了责任链模式)
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
   	  // executor 默认为空
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }else {
         // 最终是同步执行
         invokeListener(listener, event);
      }
   }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        // 默认也为null
	ErrorHandler errorHandler = getErrorHandler();
	if (errorHandler != null) {
	try {doInvokeListener(listener, event);}
		catch (Throwable err) {errorHandler.handleError(err);}
	}else {
            // 出现异常不会拦截
            doInvokeListener(listener, event);}
	}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
            // 调用ApplicationListener监听器的并传入事件
            listener.onApplicationEvent(event);
            // 如果一个监听器需要监控多个event那就需要对event再次做类型判断
	}
	catch (ClassCastException ex) {
            .......
	}
}

ApplicationListener是一个函数式接口,和SpringApplicationRunListener稍有不同的是,和SpringApplicationRunListener把事件作为了参数,让实现者去处理不同的事件(当然参数也必须维护在event中),而ApplicationListener更加细化的把不同事件对应成了不同的方法,想要处理某个事件只需要实现其中的一个方法。

所以如果一个监听器实现了ApplicationListener,但是需要监控多个event事件,那就必须在onApplicationEvent方法中对event做类型判断 以此区分事件。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

   /**
    * Handle an application event.
    * @param event the event to respond to
    */
   void onApplicationEvent(E event);
}

总结:这里获取了springboot提供的监听器,每次发布事件,会优先遍历所有实现了SpringApplicationRunListener(springboot提供)接口,然后向所有ApplicationListener(spring提供)发送事件。

springboot为项目启动提供了非常非常丰富的事件,丰富到甚至有点冗余繁琐

public interface SpringApplicationRunListener {
    // 启动前
   default void starting(ConfigurableBootstrapContext bootstrapContext) { starting();}
   @Deprecated
   default void starting() {}
    // 环境准备完成
   default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
         ConfigurableEnvironment environment) {environmentPrepared(environment)}
   @Deprecated
   default void environmentPrepared(ConfigurableEnvironment environment) {}
    // ApplicationContext构建完成时
   default void contextPrepared(ConfigurableApplicationContext context) {}
    // ApplicationContext完成加载
   default void contextLoaded(ConfigurableApplicationContext context) {}
    // ApplicationContext刷新并启动后
   default void started(ConfigurableApplicationContext context) {}
    // callRunners()之后
   default void running(ConfigurableApplicationContext context) {}
    // ApplicationContext出错时调用
   default void failed(ConfigurableApplicationContext context, Throwable exception) {}
}

由于篇幅原因,后面我会对run方法的关键步骤进行分析