SpringBoot应用启动流程源码解析

作者:TomDu 时间:2023-11-25 00:03:24 

前言

Springboot应用在启动的时候分为两步:首先生成 SpringApplication 对象 ,运行 SpringApplication 的 run 方法,下面一一看一下每一步具体都干了什么


 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
 }

创建 SpringApplication 对象


public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
//保存主配置类
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断当前是否一个web应用
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类
   this.mainApplicationClass = deduceMainApplicationClass();
 }

其中从类路径下获取到META-INF/spring.factories配置的所有ApplicationContextInitializer和ApplicationListener的具体代码如下


public final class SpringFactoriesLoader {
 /**spring.factories的位置*/
 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
 /**
 * 缓存扫描后的结果, 注意这个cache是static修饰的,说明是多个实例共享的
 * 其中MultiValueMap的key就是spring.factories中的key(比如org.springframework.boot.autoconfigure.EnableAutoConfiguration),
 * 其值就是key对应的value以逗号分隔后得到的List集合(这里用到了MultiValueMap,他是guava的一键多值map, 类似Map<String, List<String>>)
 */
 private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

private SpringFactoriesLoader() {
 }

/**
 * AutoConfigurationImportSelector及应用的初始化器和 * 里最终调用的就是这个方法,
 * 这里的factoryType是EnableAutoConfiguration.class、ApplicationContextInitializer.class、或ApplicationListener.class
 * classLoader是AutoConfigurationImportSelector、ApplicationContextInitializer、或ApplicationListener里的beanClassLoader
 */
 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   String factoryTypeName = factoryType.getName();
   return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
 }

/**
 * 加载 spring.factories文件的核心实现
 */
 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   // 先从缓存获取,如果获取到了说明之前已经被加载过
   MultiValueMap<String, String> result = cache.get(classLoader);
   if (result != null) {
     return result;
   }

try {
     // 找到所有jar中的spring.factories文件的地址
     Enumeration<URL> urls = (classLoader != null ?
         classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
         ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
     result = new LinkedMultiValueMap<>();
     // 循环处理每一个spring.factories文件
     while (urls.hasMoreElements()) {
       URL url = urls.nextElement();
       UrlResource resource = new UrlResource(url);
       // 加载spring.factories文件中的内容到Properties对象中
       Properties properties = PropertiesLoaderUtils.loadProperties(resource);
       // 遍历spring.factories内容中的所有的键值对
       for (Map.Entry<?, ?> entry : properties.entrySet()) {
         // 获得spring.factories内容中的key(比如org.springframework.boot.autoconfigure.EnableAutoConfiguratio)
         String factoryTypeName = ((String) entry.getKey()).trim();
         // 获取value, 然后按英文逗号(,)分割得到value数组并遍历
         for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
           // 存储结果到上面的多值Map中(MultiValueMap<String, String>)
           result.add(factoryTypeName, factoryImplementationName.trim());
         }
       }
     }
     cache.put(classLoader, result);
     return result;
   }
   catch (IOException ex) {
     throw new IllegalArgumentException("Unable to load factories from location [" +
         FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
 }
}

运行run方法


public ConfigurableApplicationContext run(String... args) {
//开始停止的监听
 StopWatch stopWatch = new StopWatch();
 stopWatch.start();
//声明一个可配置的ioc容器
 ConfigurableApplicationContext context = null;
 FailureAnalyzers analyzers = null;
 //配置awt相关的东西
 configureHeadlessProperty();

//获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
 SpringApplicationRunListeners listeners = getRunListeners(args);
 //回调所有的获取SpringApplicationRunListener.starting()方法
 listeners.starting();
 try {
   //封装命令行参数
  ApplicationArguments applicationArguments = new DefaultApplicationArguments(
     args);
  //准备环境
  ConfigurableEnvironment environment = prepareEnvironment(listeners,
     applicationArguments);
  //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
  Banner printedBanner = printBanner(environment);

//创建ApplicationContext;决定创建web的ioc还是普通的ioc,
//通过反射创建ioc容器((ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);)
  context = createApplicationContext();
   //出现异常之后做异常分析报告
  analyzers = new FailureAnalyzers(context);
   //准备上下文环境;将environment保存到ioc中;而且applyInitializers();
   //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
   //回调所有的SpringApplicationRunListener的contextPrepared();
   //
  prepareContext(context, environment, listeners, applicationArguments,
     printedBanner);
   //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();

//刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
   //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
  refreshContext(context);
   //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
   //ApplicationRunner先回调,CommandLineRunner再回调
  afterRefresh(context, applicationArguments);
   //所有的SpringApplicationRunListener回调finished方法
  listeners.finished(context, null);
  stopWatch.stop();
  if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass)
       .logStarted(getApplicationLog(), stopWatch);
  }
   //整个SpringBoot应用启动完成以后返回启动的ioc容器;
  return context;
 }
 catch (Throwable ex) {
  handleRunFailure(context, listeners, analyzers, ex);
  throw new IllegalStateException(ex);
 }
}

几个重要的事件回调机制

配置在META-INF/spring.factories

ApplicationContextInitializer

SpringApplicationRunListener

只需要放在ioc容器中

ApplicationRunner

CommandLineRunner

来源:https://www.cnblogs.com/tombky/p/12600191.html

标签:Spring,Boot,启动,流程
0
投稿

猜你喜欢

  • Android空心圆及层叠效果实现代码

    2021-07-04 13:10:51
  • Java注释代码执行方法解析

    2023-09-28 00:16:01
  • 猜数游戏java实现代码

    2023-01-05 18:39:28
  • 从try-with-resources到ThreadLocal,优化你的代码编写方式

    2023-11-11 03:19:52
  • Winform实现抓取web页面内容的方法

    2022-03-07 21:15:53
  • Android长按imageview把图片保存到本地的实例代码

    2021-07-23 14:20:38
  • 详解@ConfigurationProperties实现原理与实战

    2023-11-24 05:19:26
  • Android内核源码 在Ubuntu上下载,编译,安装

    2023-02-21 19:43:39
  • C#难点逐个击破(3):params数组参数

    2022-01-17 14:40:22
  • MyBatis 动态SQL和缓存机制实例详解

    2022-01-14 16:47:33
  • Opencv实现读取摄像头和视频数据

    2023-07-16 15:19:58
  • spring 自定义让@Value被解析到

    2022-12-21 13:27:49
  • SpringIOC容器Bean的作用域及生命周期实例

    2023-10-01 11:21:34
  • Android RecycleView添加head配置封装的实例

    2021-09-14 00:05:58
  • Android如何实现一个DocumentProvider示例详解

    2022-12-30 20:10:13
  • .NET实现父窗体关闭而不影响子窗体的方法

    2022-10-06 20:56:04
  • LeetCode程序员面试题之无重复字符的最长子串

    2021-09-12 05:26:36
  • 5个主流的Java开源IDE工具详解

    2021-10-13 06:06:50
  • mybatis错误之in查询 <foreach>循环问题

    2023-11-17 01:10:09
  • Android 顶部标题栏随滑动时的渐变隐藏和渐变显示效果

    2023-11-26 07:48:39
  • asp之家 软件编程 m.aspxhome.com