SpringBoot应用启动过程分析

SpringBoot项目通过SpringApplication.run(App.class, args)来启动:

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

接下来,通过源码来看看SpringApplication.run()方法的执行过程。如果对源码不感兴趣,直接下拉到文章末尾,看启动框图。

1、调用SpringApplication类的静态方法

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

2、SpringApplication对象初始化

public SpringApplication(Object... sources) {
  initialize(sources);
 }
 @SuppressWarnings({ "unchecked", "rawtypes" })
 private void initialize(Object[] sources) {
  if (sources != null && sources.length > 0) {
   this.sources.addAll(Arrays.asList(sources));
  }
  // 判断是否为WEB环境
  this.webEnvironment = deduceWebEnvironment();
  // 找到META-INF/spring.factories中ApplicationContextInitializer所有实现类,并将其实例化
  setInitializers((Collection) getSpringFactoriesInstances(
    ApplicationContextInitializer.class));
  // 找到META-INF/spring.factories中ApplicationListener所有实现类,并将其实例化
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  // 获取当前main方法类对象,即测试类中的App实例
  this.mainApplicationClass = deduceMainApplicationClass();
 }

对象初始化过程中,使用到了getSpringFactoriesInstances方法:

 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
  // 读取META-INF/spring.factories指定接口的实现类
  Set<String> names = new LinkedHashSet<String>(
    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
    classLoader, args, names);
  AnnotationAwareOrderComparator.sort(instances);
  return instances;
 }
 @SuppressWarnings("unchecked")
 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.getConstructor(parameterTypes);
    T instance = (T) constructor.newInstance(args);
    instances.add(instance);
   }
   catch (Throwable ex) {
    throw new IllegalArgumentException(
      "Cannot instantiate " + type + " : " + name, ex);
   }
  }
  return instances;
 }
 // 读取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);
  }
 }
META-INF/spring.factories文件内容,spring boot版本1.3.6.RELEASE
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# 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
# 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
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor

ApplicationListener接口是Spring框架的事件监听器,其作用可理解为SpringApplicationRunListener发布通知事件时,由ApplicationListener负责接收。SpringApplicationRunListener接口的实现类就是EventPublishingRunListener,其在SpringBoot启动过程中,负责注册ApplicationListener监听器,在不同时间节点发布不同事件类型,如果有ApplicationListener实现类监听了该事件,则接收处理。

public interface SpringApplicationRunListener {
 /**
  * 通知监听器,SpringBoot开始启动
  */
 void started();
 /**
  * 通知监听器,环境配置完成
  */
 void environmentPrepared(ConfigurableEnvironment environment);
 /**
  * 通知监听器,ApplicationContext已创建并初始化完成
  */
 void contextPrepared(ConfigurableApplicationContext context);
 /**
  * 通知监听器,ApplicationContext已完成IOC配置
  */
 void contextLoaded(ConfigurableApplicationContext context);
 /**
  * 通知监听器,SpringBoot开始完毕
  */
 void finished(ConfigurableApplicationContext context, Throwable exception);
}

附图为ApplicationListener监听接口实现类,每个类对应了一种事件。

3、SpringApplication核心run方法

/**
  * Run the Spring application, creating and refreshing a new
  * {@link ApplicationContext}.
  * @param args the application arguments (usually passed from a Java main method)
  * @return a running {@link ApplicationContext}
  */
 public ConfigurableApplicationContext run(String... args) {
  // 任务执行时间监听,记录起止时间差
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  ConfigurableApplicationContext context = null;
  configureHeadlessProperty();
  // 启动SpringApplicationRunListener监听器
  SpringApplicationRunListeners listeners = getRunListeners(args);
  listeners.started();
  try {
   ApplicationArguments applicationArguments = new DefaultApplicationArguments(
     args);
   // 创建并刷新ApplicationContext
   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);
  }
 }

这里,需要看看createAndRefreshContext()方法是如何创建并刷新ApplicationContext。

private ConfigurableApplicationContext createAndRefreshContext(
   SpringApplicationRunListeners listeners,
   ApplicationArguments applicationArguments) {
  ConfigurableApplicationContext context;
  // Create and configure the environment
  // 创建并配置运行环境,WebEnvironment与StandardEnvironment选其一
  ConfigurableEnvironment environment = getOrCreateEnvironment();
  configureEnvironment(environment, applicationArguments.getSourceArgs());
  listeners.environmentPrepared(environment);
  if (isWebEnvironment(environment) && !this.webEnvironment) {
   environment = convertToStandardEnvironment(environment);
  }
  // 是否打印Banner,就是启动程序时出现的图形
  if (this.bannerMode != Banner.Mode.OFF) {
   printBanner(environment);
  }
  // Create, load, refresh and run the ApplicationContext
  // 创建、装置、刷新、运行ApplicationContext
  context = createApplicationContext();
  context.setEnvironment(environment);
  postProcessApplicationContext(context);
  applyInitializers(context);
  // 通知监听器,ApplicationContext创建完毕
  listeners.contextPrepared(context);
  if (this.logStartupInfo) {
   logStartupInfo(context.getParent() == null);
   logStartupProfileInfo(context);
  }
  // Add boot specific singleton beans
  context.getBeanFactory().registerSingleton("springApplicationArguments",
    applicationArguments);
  // Load the sources
  // 将beans载入到ApplicationContext容器中
  Set<Object> sources = getSources();
  Assert.notEmpty(sources, "Sources must not be empty");
  load(context, sources.toArray(new Object[sources.size()]));
  // 通知监听器,beans载入ApplicationContext完毕
  listeners.contextLoaded(context);
  // Refresh the context
  refresh(context);
  if (this.registerShutdownHook) {
   try {
    context.registerShutdownHook();
   }
   catch (AccessControlException ex) {
    // Not allowed in some environments.
   }
  }
  return context;
 }

其中利用createApplicationContext()来实例化ApplicationContext对象,即DEFAULT_WEB_CONTEXT_CLASS 、DEFAULT_CONTEXT_CLASS两个对象其中一个。

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

postProcessApplicationContext(context)、applyInitializers(context)均为初始化ApplicationContext工作。

SpringBoot启动过程分析就先到这里,过程中关注几个对象:

ApplicationContext:Spring高级容器,与BeanFactory类似。

SpringApplicationRunListener:SprintBoot启动监听器,负责向ApplicationListener注册各类事件。

Environment:运行环境。

4、启动过程框图

5、接口文档

http://docs.spring.io/spring-framework/docs/current/javadoc-api/

总结

以上所述是小编给大家介绍的SpringBoot应用启动过程分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Spring Boot 启动端口如何启动

    Spring Boot 启动端口 Spring Boot 其默认是集成web容器的,启动方式由像普通Java程序一样,main函数入口启动.其内置Tomcat容器或Jetty容器,具体由配置来决定(默认Tomcat).当然你也可以将项目打包成war包,放到独立的web容器中(Tomcat.weblogic等等),当然在此之前你要对程序入口做简单调整. spring boot是个好东西,可以不用容器直接在main方法中启动,而且无需配置文件,方便快速搭建环境.可是当我们要同时启动2个springb

  • Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解

    标题和Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动很像,所以特别强调一下,这个是Tomcat对象的. 从TomcatEmbeddedServletContainer的this.tomcat.start()开始,主要是利用LifecycleBase对这一套容器(engine,host,context及wrapper)进行启动并发布诸如configure_start.before_init.after_start的lifecycleEvent事件给相应的监听器(如

  • Spring Boot启动过程完全解析(二)

    上篇给大家介绍了Spring Boot启动过程完全解析(一),大家可以点击参考下 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractApplicationContext类型并调用它的refresh方法.由于AnnotationConfigEmbeddedWebApplicationContext继承自EmbeddedWebApplicationContext,所以会

  • Spring Boot启动过程完全解析(一)

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringApplication.run(Application.class, args); --> public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[]

  • SpringBoot应用启动过程分析

    SpringBoot项目通过SpringApplication.run(App.class, args)来启动: @Configuration public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } } 接下来,通过源码来看看SpringApplication.run()方法的执行过程.如果对源码不感兴趣,直接下拉到文章末尾,看启动框图. 1.调用S

  • SpringBoot整个启动过程的分析

    前言 前一篇分析了SpringBoot如何启动以及内置web容器,这篇我们一起看一下SpringBoot的整个启动过程,废话不多说,正文开始. 正文 一.SpringBoot的启动类是**application,以注解@SpringBootApplication注明. @SpringBootApplication public class CmsApplication { public static void main(String[] args) { SpringApplication.run

  • SpringBoot应用启动内置Tomcat的过程源码分析

    Connector启动过程 Connector是Tomcat提供的类. // 通过此 Connector 开始处理请求 @Override protected void startInternal() throws LifecycleException { // Validate settings before starting if (getPortWithOffset() < 0) { throw new LifecycleException(sm.getString( "coyote

  • Angularjs的启动过程分析

    本文介绍了Angularjs的启动过程分析,分享给大家 启动过程(v1.3.9) 步骤一 用自执行函数的形式让整个代码在加载完成之后立即执行 in angular.js Line6 (function(window, document, undefined) 在window上暴露一个唯一的全局对象angular,Line250 angular = window.angular || (window.angular = {}) 获得其它工具模块 Line 2129 function publish

  • SpringBoot Tomcat启动实例代码详解

    废话不多了,具体内容如下所示: Application configuration class: @SpringBootApplication public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return appli

  • Android Service的启动过程分析

    Android Service的启动过程分析 刚开始学习Service的时候以为它是一个线程的封装,也可以执行耗时操作.其实不然,Service是运行在主线程的.直接执行耗时操作是会阻塞主线程的.长时间就直接ANR了. 我们知道Service可以执行一些后台任务,是后台任务不是耗时的任务,后台和耗时是有区别的喔. 这样就很容易想到音乐播放器,天气预报这些应用是要用到Service的.当然如果要在Service中执行耗时操作的话,开个线程就可以了. 关于Service的运行状态有两种,启动状态和绑

  • springBoot项目启动类启动无法访问的解决方法

    网上也查了一些资料,我这里总结.下不来虚的,也不废话. 解决办法: 1.若是maven项目,则找到右边Maven Projects --->Plugins--->run(利用maven启动)则可以加载到webapp资源 2.上面方法治标不治本.在项目的pom文件中添加<bulid>标签标注路径即可,pom.xml后部分代码如下: 刷新maven加载,重启项目.若还是无法访问,重新导入项目 <dependencies> xxxxxxxxxxxx </dependen

  • 详解SpringBoot应用服务启动与安全终止

    SpringBoot应用服务启动 参照官方示例工程可以快速搭建简单SpringBoot应用,官方连接如下:http://projects.spring.io/spring-boot/#quick-start 闲话少叙,上代码: package hello; import org.springframework.boot.*; import org.springframework.boot.autoconfigure.*; import org.springframework.stereotype

  • SpringBoot同时启动不同端口图示解析

    这篇文章主要介绍了SpringBoot同时启动不同端口图示解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.首先右键点击需要同时启动的应用,选择复制配置 或者: 2. 在配置窗口中进行修改 3. 保存后如下,可以右键逐一启动 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们.

  • SpringBoot应用启动流程源码解析

    前言 Springboot应用在启动的时候分为两步:首先生成 SpringApplication 对象 ,运行 SpringApplication 的 run 方法,下面一一看一下每一步具体都干了什么 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);

随机推荐