文章目录

  • 一、创建SpringApplication对象
  • (1)外部run()
  • (2)new SpringApplication()
  • (3)initialize()
  • (3.1)sources
  • (3.2)deduceWebEnvironment()
  • (3.3)setInitializers()
  • getSpringFactoriesInstances()
  • createSpringFactoriesInstances()
  • (3.4)setListeners()
  • (3.5)deduceMainApplicationClass
  • (3.6)总结
  • 二、调用SpringApplication.run()
  • (1)SpringApplicationRunListeners


SpringBoot的启动流程虽然复杂,但是认真分析还是可以做出重要且有代表性的总结的!接下来,我们从一个简单的SpringBoot应用对SpringBoot的启动流程进行分析!

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

一、创建SpringApplication对象

(1)外部run()

首先,调用run()方法

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

随后进入重构run()方法:

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

创建一个SpringApplication对象出来,然后调用对象的run()方法,我们先看看这个创建SpringApplication的过程:


(2)new SpringApplication()

SpringApplication的构造函数,其中调用了initialize()方法!

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

看下initialize()方法的具体逻辑:

(3)initialize()

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

主要逻辑:初始化SpringApplication对象的成员变量(sources,webEnvironment,initializers,listeners,mainApplicationClass),接下来,分析每一个初始化动作!

(3.1)sources

保存主配置类…

if(sources != null && sources.length > 0) {
  this.sources.addAll(Arrays.asList(sources));
}

(3.2)deduceWebEnvironment()

this.webEnvironment = deduceWebEnvironment();
private boolean deduceWebEnvironment() {
  for(String className : WEB_ENVIRONMENT_CLASSES) {
    if(!ClassUtils.isPresent(className, null)) {
        return false;
    }
  }
  return true;
}

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

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

逻辑很简单:根据org.springframework.util.ClassUtils的静态方法去判断classpath下是否有WEB_ENVIRONMENT_CLASSES包含的类

  • 有包含则返回true则表示启动一个WEB应用
  • 否则返回false启动一个标准Spring的应用。

(3.3)setInitializers()

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

initializers成员变量是一个ApplicationContextInitializer类型对象的集合。顾名思义,ApplicationContextInitializer是一个可以用来初始化ApplicationContext的接口。

private List<ApplicationContextInitializer<?>> initializers;

private void initialize(Object[] sources) {
    ...
    // 为成员变量initializers赋值
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    ...
}
public void setInitializers(Collection<? extends
					 ApplicationContextInitializer<?>> initializers) {
    this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
    this.initializers.addAll(initializers);
}

初始化classpath下的所有的可用的应用上下文初始化器ApplicationContextInitializer

关键是调用getSpringFactoriesInstances(ApplicationContextInitializer.class),来获取ApplicationContextInitializer类型对象的列表

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()、createSpringFactoriesInstances()

getSpringFactoriesInstances() 方法中,

1. 首先通过调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)来获取所有Spring Factories的名字存入一个Set中.

2. 然后调用createSpringFactoriesInstances()方法根据读取到的名字创建对象。
3. 最后会将创建好的对象列表排序并返回。 ----> 我们看一下黄色标记的方法

getSpringFactoriesInstances()
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
  String factoryClassName = factoryClass.getName();
  try {
    Enumeration<URL> urls = (classLoader != null ?
    	 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
         ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    List<String> result = new ArrayList<String>();
    while(urls.hasMoreElements()) {
      URL url = urls.nextElement();
      Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
      String factoryClassNames = properties.getProperty(factoryClassName);
      result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    }
    return result;
  }
  catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + 
  		factoryClass.getName() + "] factories from location [" + 
  		FACTORIES_RESOURCE_LOCATION + "]", ex);
  }
}

从一个名字叫spring.factories的资源文件中,读取key为org.springframework.context.ApplicationContextInitializer的value。spring.factories的部分内容如下:

"摘自spring-boot-1.3.3.RELEASE.jar中的资源文件META-INF/spring.factories"
"# Application Context Initializers"
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context."ConfigurationWarningsApplicationContextInitializer",\
org.springframework.boot.context."ContextIdApplicationContextInitializer",\
org.springframework.boot.context.config."DelegatingApplicationContextInitializer",\
org.springframework.boot.context.web."ServerPortInfoApplicationContextInitializer"

仍然是在getSpringFactoriesInstances()方法中:接下来会调用

createSpringFactoriesInstances()
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()); //1
  for(String name : names) {
    try {
      Class<?> instanceClass = ClassUtils.forName(name, classLoader);
      Assert.isAssignable(type, instanceClass);
      Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);
      T instance = (T) constructor.newInstance(args);
      instances.add(instance);
    } catch (Throwable ex) { throw new IllegalArgumentException(
                "Cannot instantiate " + type + " : " + name, ex);
    }
  }
  return instances;
}

在1处可以看到:

SpringApplication对象的成员变量initalizers就被初始化为,ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer这四个类的对象组成的list(ArrayList)。


(3.4)setListeners()

结论:初始化classpath下的所有的可用的ApplicationListener

setListeners((Collection) 
			getSpringFactoriesInstances(ApplicationListener.class));
"摘自:org.springframework.boot.SpringApplication"
private List<ApplicationListener<?>> listeners;

private void initialize(Object[] sources) {
  // 为成员变量listeners赋值
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  ...
}
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
  this.listeners = new ArrayList<ApplicationListener<?>>();
  this.listeners.addAll(listeners);
}

listeners成员变量,是一个ApplicationListener<?>类型对象的集合。可以看到获取该成员变量内容使用的是跟成员变量initializers一样的方法,只不过传入的类型从ApplicationContextInitializer.class变成了ApplicationListener.class。

"摘自spring-boot-1.3.3.RELEASE.jar中的资源文件META-INF/spring.factories"
"# Application Listeners"
org.springframework.context.ApplicationListener=\
org.springframework.boot.builder."ParentContextCloserApplicationListener",\
org.springframework.boot.context."FileEncodingApplicationListener",\
org.springframework.boot.context.config."AnsiOutputApplicationListener",\
org.springframework.boot.context.config."ConfigFileApplicationListener",\
org.springframework.boot.context.config."DelegatingApplicationListener",\
org.springframework.boot.liquibase."LiquibaseServiceLocatorApplicationListener",\
org.springframework.boot.logging."ClasspathLoggingApplicationListener",\
org.springframework.boot.logging."LoggingApplicationListener"

listener最终会被初始化为以上这几个类的对象组成的list。


(3.5)deduceMainApplicationClass

"代码摘自:org.springframework.boot.SpringApplication"
private Class<?> mainApplicationClass;

private void initialize(Object[] sources) {
  ...
  // 为成员变量mainApplicationClass赋值
  this.mainApplicationClass = deduceMainApplicationClass();
  ...
}
private Class<?> deduceMainApplicationClass() {
  try {
    StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
    for(StackTraceElement stackTraceElement : stackTrace) {
      if("main".equals(stackTraceElement.getMethodName())) { //1
        return Class.forName(stackTraceElement.getClassName());
      }
    }
  }
  catch (ClassNotFoundException ex) {
    // Swallow and continue
  }
  return null;
}

在该方法中,通过获取当前调用栈,找到入口方法main所在的类,并将其赋值给SpringApplication对象的成员变量mainApplicationClass。在我们的例子中mainApplicationClass即是我们自己编写的Application类。

1处:从多个配置类中寻找有main()方法的主配置类

至此,SpringApplication对象就创建出来了!

(3.6)总结

1) 保存主配置类(也就是示例中的HelloWorldMainApplication)

2) 判断当前是否一个web应用(是否导入web模块)

3) 从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来

4) 从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener

5) 从多个配置类中找到有main方法的主配置类


二、调用SpringApplication.run()

"摘自:org.springframework.boot.SpringApplication"
public ConfigurableApplicationContext run(String... args) {
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  ConfigurableApplicationContext context = null;
  configureHeadlessProperty();
  SpringApplicationRunListeners listeners = getRunListeners(args);
  listeners.started();
  
  try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
    context = createAndRefreshContext(listeners, applicationArguments);
    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, ex);
     throw new IllegalStateException(ex);
  }
}

StopWatch是来自org.springframework.util的工具类,可以用来方便的记录程序的运行时间。

关注点:为以下动作

(1)SpringApplicationRunListeners

"以下代码摘自:org.springframework.boot.SpringApplication"
public ConfigurableApplicationContext run(String... args) {
  // 0 
  SpringApplicationRunListeners listeners = getRunListeners(args);
  // 0.1
  listeners.started();
  try {...
    // 1 + 1.1
    ConfigurableEnvironment environment = 
          prepareEnvironment(listeners, applicationArguments);
    // 2
    context = createApplicationContext();
    // 3
    prepareContext(context, environment, listeners, applicationArguments,
          printedBanner);
    //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
    // 4 
    refreshContext(context);
    // 5
    afterRefresh(context, applicationArguments);
    //所有的SpringApplicationRunListener回调finished方法
    listeners.finished(context, null);
  }...
  // 6
  return context;
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
  Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
  return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
         SpringApplicationRunListener.class, types, this, args));
}

主要步骤:

0) 获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories

0.1) 回调所有的获取SpringApplicationRunListener.starting()方法

1) 准备环境

1.1) 创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成

2) 创建ApplicationContext;决定创建web的ioc还是普通的ioc

3) 准备上下文环境;将environment保存到ioc中;• applyInitializers():迭代地回调之前保存的所有的ApplicationContextInitializer的initialize()方法
• 回调所有的SpringApplicationRunListener的contextPrepared();
4) 刷新容器;ioc容器初始化扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
• (如果是web应用还会创建嵌入式的Tomcat);
5) 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
 //ApplicationRunner先回调,CommandLineRunner再回调



6) 整个SpringBoot应用启动完成以后返回启动的ioc容器;