热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

SpringBoot启动过程的实现

这篇文章主要介绍了SpringBoot启动过程的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

SpringBoot启动过程分析,首先打开SpringBoot的启用入口Main类:

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

可以看到main方法里面只有一行核心启用类:SpringApplication.run(ApplicationMain.class, args);这个是关键,在改行打上断点,debug模式启动该main类。点击下一步进入SpringApplication的源码对应的run方法:

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

初始化SpringApplication

SpringApplication实例化之前会调用构造方法进行初始化:

public SpringApplication(Object... sources) {
  this.bannerMode = Mode.CONSOLE;
  this.logStartupInfo = true;
  this.addCommandLineProperties = true;
  this.headless = true;
  this.registerShutdownHook = true;
  this.additiOnalProfiles= new HashSet();
  this.initialize(sources);
 }

而SpringApplication构造方法的核心是:this.initialize(sources);初始化方法,SpringApplication通过调用该方法来初始化。

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.deduceWebEnvironment方法是用来判断当前应用的环境,该方法通过获取这两个类来判断当前环境是否是web环境,如果能获得这两个类说明是web环境,否则不是。

javax.servlet.Servlet
org.springframework.web.context.ConfigurableWebApplicationContext

2.getSpringFactoriesInstances方法主要用来从spring.factories文件中找出key为ApplicationContextInitializer的类并实例化,然后调用setInitializers方法设置到SpringApplication的initializers属性中。这个过程就是找出所有的应用程序初始化器。

private  Collection<&#63; extends T> getSpringFactoriesInstances(Class type, Class<&#63;>[] parameterTypes, Object... args) {
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  Set names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  List instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
  AnnotationAwareOrderComparator.sort(instances);
  return instances;
 }
public static List loadFactoryNames(Class<&#63;> factoryClass, ClassLoader classLoader) {
  String factoryClassName = factoryClass.getName();

  try {
   //从spring.factories文件中找出key为ApplicationContextInitializer的类
   Enumeration urls = classLoader != null &#63; classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
   ArrayList result = new ArrayList();

   while(urls.hasMoreElements()) {
    URL 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 var8) {
   throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
  }
 }

当前的初始化器有如下几个:

在这里插入图片描述

3.同理调用getSpringFactoriesInstances从spring.factories文件中找出key为ApplicationListener的类并实例化,然后调用setListeners方法设置到SpringApplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器。
当前的事件监听器有如下几个:

在这里插入图片描述

4.调用deduceMainApplicationClass方法找出main类,就是这里的ApplicationMain类。

运行SpringApplication

初始化SpringApplication完成之后,调用run方法运行:

public ConfigurableApplicationContext run(String... args) {
  //计时器,统计任务的执行时间
  StopWatch stopWatch = new StopWatch();
  //开始执行
  stopWatch.start();
  ConfigurableApplicationContext cOntext= null;
  FailureAnalyzers analyzers = null;
  this.configureHeadlessProperty();
  // 获取SpringApplicationRunListeners启动事件监听器,这里只有一个EventPublishingRunListener
  SpringApplicationRunListeners listeners = this.getRunListeners(args);
  // 封装成SpringApplicationEvent事件然后广播出去给SpringApplication中的listeners所监听
  listeners.starting();

  try {
   // 构造一个应用程序参数持有类
   ApplicationArguments applicatiOnArguments= new DefaultApplicationArguments(args);
   // 准备并配置环境
   ConfigurableEnvironment envirOnment= this.prepareEnvironment(listeners, applicationArguments);
    // 打印banner图形
   Banner printedBanner = this.printBanner(environment);
   // 创建Spring容器
   cOntext= this.createApplicationContext();
   new FailureAnalyzers(context);
   // 配置Spring容器
   this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
   // 容器上下文刷新
   this.refreshContext(context);
   // 容器创建完成之后调用afterRefresh方法
   this.afterRefresh(context, applicationArguments);
   // 调用监听器,广播Spring启动结束的事件
   listeners.finished(context, (Throwable)null);
   // 停止计时器
   stopWatch.stop();
   if (this.logStartupInfo) {
    (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
   }

   return context;
  } catch (Throwable var9) {
   this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
   throw new IllegalStateException(var9);
  }
 }

SpringApplicationRunListeners

1.获取启动事件监听器,可以看看该方法:

SpringApplicationRunListeners listeners = this.getRunListeners(args);

private SpringApplicationRunListeners getRunListeners(String[] args) {
  Class<&#63;>[] types = new Class[]{SpringApplication.class, String[].class};
  return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
 }

同样的通过调用getSpringFactoriesInstances方法去META-INF/spring.factories文件中拿到SpringApplicationRunListener监听器,当前的SpringApplicationRunListener事件监听器只有一个EventPublishingRunListener广播事件监听器:

在这里插入图片描述

SpringApplicationRunListeners内部持有SpringApplicationRunListener集合和1个Log日志类。用于SpringApplicationRunListener监听器的批量执行。

SpringApplicationRunListener用于监听SpringApplication的run方法的执行,它定义了5个步骤:

1.starting:run方法执行的时候立马执行,对应的事件类型是ApplicationStartedEvent
2.environmentPrepared:ApplicationContext创建之前并且环境信息准备好的时候调用,对应的事件类型是ApplicationEnvironmentPreparedEvent
3.contextPrepared:ApplicationContext创建好并且在source加载之前调用一次,没有具体的对应事件
4.contextLoaded:ApplicationContext创建并加载之后并在refresh之前调用,对应的事件类型是ApplicationPreparedEvent
5.finished:run方法结束之前调用,对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent

SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener,详见获取SpringApplicationRunListeners。它把监听的过程封装成了SpringApplicationEvent事件并让内部属性ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。

所以说SpringApplicationRunListener和ApplicationListener之间的关系是通过ApplicationEventMulticaster广播出去的SpringApplicationEvent所联系起来的

2.启动事件监听器

通过listeners.starting()可以启动事件监听器SpringApplicationRunListener ,SpringApplicationRunListener 是一个启动事件监听器接口:

public interface SpringApplicationRunListener {
 void starting();

 void environmentPrepared(ConfigurableEnvironment var1);

 void contextPrepared(ConfigurableApplicationContext var1);

 void contextLoaded(ConfigurableApplicationContext var1);

 void finished(ConfigurableApplicationContext var1, Throwable var2);
}

SpringApplicationRunListener 接口的具体实现就是EventPublishingRunListener类,我们主要来看一下它的startting方法,该方法会封装成SpringApplicationEvent事件然后广播出去给SpringApplication中的listeners所监听。

public void starting() {
  this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
 }

配置并准备环境

private ConfigurableEnvironment prepareEnvironment(
  SpringApplicationRunListeners listeners,
  ApplicationArguments applicationArguments) {
 // 创建应用程序的环境信息。如果是web程序,创建StandardServletEnvironment;否则,创建StandardEnvironment
 ConfigurableEnvironment envirOnment= getOrCreateEnvironment();
 // 配置环境信息。比如profile,命令行参数
 configureEnvironment(environment, applicationArguments.getSourceArgs());
 // 广播出ApplicationEnvironmentPreparedEvent事件给相应的监听器执行
 listeners.environmentPrepared(environment);
 // 环境信息的校对
 if (!this.webEnvironment) {
  envirOnment= new EnvironmentConverter(getClassLoader())
    .convertToStandardEnvironmentIfNecessary(environment);
 }
 return environment;
}

判断环境,如果是web程序,创建StandardServletEnvironment;否则,创建StandardEnvironment。

private ConfigurableEnvironment getOrCreateEnvironment() {
  if (this.environment != null) {
   return this.environment;
  } else {
   return (ConfigurableEnvironment)(this.webEnvironment &#63; new StandardServletEnvironment() : new StandardEnvironment());
  }
 }

创建Spring容器上下文

protected ConfigurableApplicationContext createApplicationContext() {
 Class<&#63;> cOntextClass= this.applicationContextClass;
 if (cOntextClass== null) {
  try {
   // 判断是否是web应用,
   // 如果是则创建AnnotationConfigEmbeddedWebApplicationContext,否则创建AnnotationConfigApplicationContext
   cOntextClass= Class.forName(this.webEnvironment
     &#63; 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容器上下文

private void prepareContext(ConfigurableApplicationContext context,
  ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
  ApplicationArguments applicationArguments, Banner printedBanner) {
 // 设置Spring容器上下文的环境信息
 context.setEnvironment(environment);
 // Spring容器创建之后做一些额外的事
 postProcessApplicationContext(context);
 // SpringApplication的初始化器开始工作
 applyInitializers(context);
 // 遍历调用SpringApplicationRunListener的contextPrepared方法。目前只是将这个事件广播器注册到Spring容器中
 listeners.contextPrepared(context);
 if (this.logStartupInfo) {
  logStartupInfo(context.getParent() == null);
  logStartupProfileInfo(context);
 }

 // 把应用程序参数持有类注册到Spring容器中,并且是一个单例
 context.getBeanFactory().registerSingleton("springApplicationArguments",
   applicationArguments);
 if (printedBanner != null) {
  context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
 }

 // 加载sources,sources是main方法所在的类
 Set sources = getSources();
 Assert.notEmpty(sources, "Sources must not be empty");
 // 将sources加载到应用上下文中。最终调用的是AnnotatedBeanDefinitionReader.registerBean方法
 load(context, sources.toArray(new Object[sources.size()]));
 // 广播出ApplicationPreparedEvent事件给相应的监听器执行
 // 执行EventPublishingRunListener.contextLoaded方法
 listeners.contextLoaded(context);
}

Spring容器创建之后回调方法postProcessApplicationContext

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
 // 如果SpringApplication设置了实例命名生成器,则注册到Spring容器中
 if (this.beanNameGenerator != null) {
  context.getBeanFactory().registerSingleton(
    AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
    this.beanNameGenerator);
 }
 // 如果SpringApplication设置了资源加载器,设置到Spring容器中
 if (this.resourceLoader != null) {
  if (context instanceof GenericApplicationContext) {
   ((GenericApplicationContext) context)
     .setResourceLoader(this.resourceLoader);
  }
  if (context instanceof DefaultResourceLoader) {
   ((DefaultResourceLoader) context)
     .setClassLoader(this.resourceLoader.getClassLoader());
  }
 }
}

初始化器开始工作

protected void applyInitializers(ConfigurableApplicationContext context) {
 // 遍历每个初始化器,调用对应的initialize方法
 for (ApplicationContextInitializer initializer : getInitializers()) {
  Class<&#63;> requiredType = GenericTypeResolver.resolveTypeArgument(
    initializer.getClass(), ApplicationContextInitializer.class);
  Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
  initializer.initialize(context);
 }
}

Spring容器创建完成之后会调用afterRefresh方法

ApplicationRunner、CommandLineRunner类都是在在afterRefresh方法中调用的,也就是说在Spring容器创建之后执行的。

protected void applyInitializers(ConfigurableApplicationContext context) {
 // 遍历每个初始化器,调用对应的initialize方法
 for (ApplicationContextInitializer initializer : getInitializers()) {
  Class<&#63;> requiredType = GenericTypeResolver.resolveTypeArgument(
    initializer.getClass(), ApplicationContextInitializer.class);
  Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
  initializer.initialize(context);
 }
}

参考:https://blog.wangqi.love/articles/Spring/SpringBoot启动过程.html

到此这篇关于SpringBoot启动过程的实现的文章就介绍到这了,更多相关SpringBoot启动过程内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
  • 本文探讨了在Linux系统上使用Docker时,通过volume将主机上的HTML5文件挂载到容器内部指定目录时遇到的403错误,并提供了解决方案和详细的操作步骤。 ... [详细]
  • Vue 2 中解决页面刷新和按钮跳转导致导航栏样式失效的问题
    本文介绍了如何通过配置路由的 meta 字段,确保 Vue 2 项目中的导航栏在页面刷新或内部按钮跳转时,始终保持正确的 active 样式。具体实现方法包括设置路由的 meta 属性,并在 HTML 模板中动态绑定类名。 ... [详细]
  • 使用Numpy实现无外部库依赖的双线性插值图像缩放
    本文介绍如何仅使用Numpy库,通过双线性插值方法实现图像的高效缩放,避免了对OpenCV等图像处理库的依赖。文中详细解释了算法原理,并提供了完整的代码示例。 ... [详细]
  • 本文详细介绍了 BERT 模型中 Transformer 的 Attention 机制,包括其原理、实现代码以及在自然语言处理中的应用。通过结合多个权威资源,帮助读者全面理解这一关键技术。 ... [详细]
  • QUIC协议:快速UDP互联网连接
    QUIC(Quick UDP Internet Connections)是谷歌开发的一种旨在提高网络性能和安全性的传输层协议。它基于UDP,并结合了TLS级别的安全性,提供了更高效、更可靠的互联网通信方式。 ... [详细]
  • QBlog开源博客系统:Page_Load生命周期与参数传递优化(第四部分)
    本教程将深入探讨QBlog开源博客系统的Page_Load生命周期,并介绍一种简洁的参数传递重构方法。通过视频演示和详细讲解,帮助开发者更好地理解和应用这些技术。 ... [详细]
  • PyCharm下载与安装指南
    本文详细介绍如何从官方渠道下载并安装PyCharm集成开发环境(IDE),涵盖Windows、macOS和Linux系统,同时提供详细的安装步骤及配置建议。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 本文探讨了如何像程序员一样思考,强调了将复杂问题分解为更小模块的重要性,并讨论了如何通过妥善管理和复用已有代码来提高编程效率。 ... [详细]
  • 火星商店问题:线段树分治与持久化Trie树的应用
    本题涉及编号为1至n的火星商店,每个商店有一个永久商品价值v。操作包括每天在指定商店增加一个新商品,以及查询某段时间内某些商店中所有商品(含永久商品)与给定密码值的最大异或结果。通过线段树分治和持久化Trie树来高效解决此问题。 ... [详细]
  • Java 中的 BigDecimal pow()方法,示例 ... [详细]
  • 本文总结了汇编语言中第五至第八章的关键知识点,涵盖间接寻址、指令格式、安全编程空间、逻辑运算指令及数据重复定义等内容。通过详细解析这些内容,帮助读者更好地理解和应用汇编语言的高级特性。 ... [详细]
  • 探讨如何高效使用FastJSON进行JSON数据解析,特别是从复杂嵌套结构中提取特定字段值的方法。 ... [详细]
  • 本文详细介绍了如何使用Maven高效管理多模块项目,涵盖项目结构设计、依赖管理和构建优化等方面。通过具体的实例和配置说明,帮助开发者更好地理解和应用Maven在复杂项目中的优势。 ... [详细]
author-avatar
zhangsheng7_215
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有