SpringBoot(一)启动相关【死磕源码】

1,208 阅读3分钟

上篇提到了启动过程中的一些事件,这篇就来翻源码来了解下这些事件,废话不多少,整起。。

SpringApplication.run(new Object[]{
         WebApiThirdpartyApplication.class
    }, args);

每个springboot项目启动都是从这开始,通过调用SpringApplication.run()启动,接下来看看run都做了啥

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

实例SpringApplication,通过构造器去调用初始化方法

public SpringApplication(Object... sources) {
	initialize(sources);
}

上篇说事件的时候提到过 一个事件是在监听注册和初始化自后执行的,除了这两个操作在任何处理之前

private void initialize(Object[] sources) {
	if (sources != null && sources.length > 0) {
		this.sources.addAll(Arrays.asList(sources));
	}
	this.webEnvironment = deduceWebEnvironment();
	setInitializers((Collection) getSpringFactoriesInstances(
		ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

接下来看下 这个初始化做了几件事

  1. this.webEnvironment = deduceWebEnvironment();,判断应用上下问环境
 private boolean deduceWebEnvironment() {
	for (String className : WEB_ENVIRONMENT_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return false;
		}
	}
	return true;
}

其中WEB_ENVIRONMENT_CLASSES是一个静态常量数组:

private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
         "org.springframework.web.context.ConfigurableWebApplicationContext" };

可以看到是根据org.springframework.util.ClassUtils的静态方法去判断classpath里面是否有WEB_ENVIRONMENT_CLASSES包含的类,如果有都包含则返回true则表示启动一个WEB应用,否则返回false启动一个标准Spring的应用。

  1. setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));

这个方法则是初始化classpath下的所有的可用的ApplicationContextInitializer。 3. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 这个方法则是初使化classpath下的所有的可用的ApplicationListener。 4. this.mainApplicationClass = deduceMainApplicationClass();,进入方法看下

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方法的全类名并返回其实例并设置到SpringApplicationthis.mainApplicationClass完成初始化。然后调用SpringApplication实例的run方法来启动应用。代码如下:

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.started();
   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);
   }
}

这里我们看以下代码:

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);

首先是获取启动时传入参数args并初始化为ApplicationArguments对象,SpringApplication.run(Application.class, args);取这里传入值。 然后配置SpringBoot应用的环境:

ConfigurableEnvironment environment = prepareEnvironment(listeners,
                  applicationArguments);

Banner printedBanner = printBanner(environment); 打印标志这个方法不说明,因为没有什么实质性作用,如果想玩玩修改一下标志,那可以在项目的classpath下新建一个banner.txt文件。

然后下面代码就是比较核心的:

context = createApplicationContext();
   analyzers = new FailureAnalyzers(context);
   prepareContext(context, environment, listeners, applicationArguments,
          printedBanner);
   refreshContext(context);
   afterRefresh(context, applicationArguments);

首先是createApplicationContext()方法:

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) {
              thrownew IllegalStateException(
                     "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                     ex);
           }
       }
       return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
    }

可以看出根据之前初始化过程初始化的this.webEnvironment来决定初始化一个什么容器。如果classpath下是否有javax.servlet.Servlet和 org.springframework.web.context.ConfigurableWebApplicationContext类, 则使用DEFAULT_WEB_CONTEXT_CLASS初始化容器,如果不存在则用DEFAULT_CONTEXT_CLASS初始化容器。

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";
 
    /**
     * The class name of application context that will be used by default for web
     * environments.
     */
    publicstaticfinal String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";

以上是代码指定了容器的类名,最后通过Spring的工具类初始化容器类bean BeanUtils.instantiate(contextClass);

完成容器的创建工作。然后执行以下的几个步骤完成整个容器的创建与启动以及bean的注入功能。

prepareContext(context, environment, listeners, applicationArguments,
                  printedBanner);

以下这一句代码是实现spring-boot-starter-*的自动化配置的关键。

refreshContext(context); afterRefresh(context, applicationArguments);

至此通过SpringBoot启动的容器已经构造完成。这里忽略了启动流程中的收集各种Listener,创建Environment及Environment的初始化的。

下篇准备整理下自动配置,敬请关注