Spring Boot源码分析三:启动流程

本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。

createSpringFactoriesInstances

上文说到读取配置文件spring.factories时,将读取到配置文件中的类进行实例化,现在来看这个方法。

java 复制代码
// 参数分别是 类型,参数类型,类加载器,参数,类名称
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
      ClassLoader classLoader, Object[] args, Set<String> names) {
      // 初始化一个与类名集合大小一样的集合
   List<T> instances = new ArrayList<>(names.size());
   // 遍历类名集合逐一实例化
   for (String name : names) {
      try {
         // 这里又实用到了ClassUtils,使用这个工具类获取到这个类名称字符串的类型。这个工具类的forName方法的写法很好,我在很多方法都有看到类似的影子。
         // 逻辑大概就是先判断是不是基础类型,如果不是就用类加载器进行加载
         // 只要是我们自己定义的类就是这样Class.forName(name, false, clToUse);
         Class<?> instanceClass = ClassUtils.forName(name, classLoader);
         // 判断type这个Class是不是instanceClass的超类
         Assert.isAssignable(type, instanceClass);
         // 找到Class的指定参数类型的构造方法
         Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
         // 通过构造方法和参数实例化一个类的对象。这又有一个工具类了
         T instance = (T) BeanUtils.instantiateClass(constructor, args);
         // 保存实力对象
         instances.add(instance);
      }
      catch (Throwable ex) {
         throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
      }
   }
   return instances;
}

实例化SpringApplication的构造方法分析完了,接下来就看看SpringApplication的成员方法run方法的逻辑。

run

这个方法有点多,不同版本也有一定的差异。

java 复制代码
public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
   long startTime = System.nanoTime();
   // 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
      listeners.started(context, timeTakenToStartup);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

createBootstrapContext

java 复制代码
private DefaultBootstrapContext createBootstrapContext() {
// 通过无参构造实例化了一个对象
   DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
   // 遍历SpringApplication的构造方法中读取spring.factories配置文件中BootStrapRegistryInitializer并实例化的对象,并调用它们的initialize方法,这是一个扩展点哟。如果我们给spring.factories中添加BootStrapRegistryInitializer的配置在这里也会执行
   this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
   return bootstrapContext;
}
相关推荐
橙序员小站2 小时前
Agent Skill 是什么?一文讲透 Agent Skill 的设计与实现
前端·后端
怒放吧德德2 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆4 小时前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
开心就好20255 小时前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios
悟空码字6 小时前
告别“屎山代码”:AI 代码整洁器让老项目重获新生
后端·aigc·ai编程
小码哥_常6 小时前
大厂不宠@Transactional,背后藏着啥秘密?
后端
奋斗小强6 小时前
内存危机突围战:从原理辨析到线上实战,彻底搞懂 OOM 与内存泄漏
后端
小码哥_常6 小时前
Spring Boot接口防抖秘籍:告别“手抖”,守护数据一致性
后端
心之语歌6 小时前
基于注解+拦截器的API动态路由实现方案
java·后端
None3216 小时前
【NestJs】基于Redlock装饰器分布式锁设计与实现
后端·node.js