Spring Boot 原理之初始化篇

Spring Boot 原理之初始化篇

通常在String Boot 中通以下如下代码启动Spring Boot。
在这里插入图片描述
代码很简洁,通过一行代码就能启动整个Spring Boot工程。在这行代码中通过调用SpringApplication静态方法run,并传入启动类class和主函数参数来启动。在这里插入图片描述
在润方法中又将启动类class封装成数组,并用这个数组构造了一个SpringApplication。我们看下SpringApplication构造函数。
在这里插入图片描述
在构造函数中,调用了一个初始化方法initialize,这个初始化方法将是本篇文章的主要内容。
在这里插入图片描述
这个初始化方法中主要完成了5件事。

  1. 将启动类class加到 sources属性中;
  2. 推断是否为Web环境;
  3. 创建初始化器;
  4. 创建监听器;
  5. 推断启动类class;

推断是否为Web环境

在这里插入图片描述
在这里插入图片描述
从上面程序中可以看出,如果项目中同时存在javax.servlet.Servlet和
org.springframework.web.context.ConfigurableWebApplicationContext这两个class可以推断项目是属于Web应用。

创建初始化器和创建监听器

这里把把它们放在一起分析,应为它们使用的机制相同。
在这里插入图片描述

从上面的代码可以看出,它们都调用了getSpringFactoriesInstances这个方法。只是传参不同,创建初始化器时传的是ApplicationContextInitializer class,创建监听器时,传入的参数是ApplicationListener class。
在这里插入图片描述
首先来分析一下这个参数的形式, 传入一个泛型为T的class,返回一个T的实例集合。可以大胆猜测下,传入的type为一个接口的class,返回实现了该接口的所有示例。那这些示例怎么来的呢?我们看下getSpringFactoriesInstances的方法另一个实现功能的重载形式。
在这里插入图片描述
在该方法中调用了SpringFactoriesLoader的静态方法loadFactoryNames。
在这里插入图片描述
在这里插入图片描述
看一下这个方法的注释。使用既定的类加载器从路径FACTORIES_RESOURCE_LOCATION中加载实现了给定类型的类的全类名。FACTORIES_RESOURCE_LOCATION的值为META-INF/spring.factories。这个方法在Spring Boot中十分十分十分重要。
这里是Spring Boot的SPI(Service Provider Interface)机制,为某个接口寻找服务实现的。
Java本身也有原生SPI,在Java中如果要使用SPI,需要在jar包的META-INF/services/目录里创建一个以服务接口命名的文件,并在这个文件中写入接口的实现类的全限定名。然后通过serviceLoader加载实现类并调用。
Dubbo并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。
Sping Boot也没有使用Java SPI,而是通过上面的方法,Spring Boot找到META-INF/spring.factories中实现某个接口的类名,再通过反射的方式创建这些类。比如在Spring Boot jar包中文件META-INF/spring.factories中定义监听器接口SpringApplication.class的实现类有:
在这里插入图片描述
初始化器ApplicationContextInitializer.class的时间接口有:
在这里插入图片描述
至于这些实现类具体作用这里不展开讨论。

来看下SpringFactoriesLoader静态方法loadFactoryNames如何实现。
首先通过这行代码
在这里插入图片描述
使用Java原生方法找到所有jar包中的META-INF/spring.factories文件位置。然后将每个文件META-INF/spring.factories加载成Java Properties, 这个就可以以Key-Value的形式读取文件内容,再以接口全类名为Key获取该接口实现类类名。最后通过StringUtils.commaDelimitedListToStringArray方法将以逗号拼接实现类类名转换成数组,并将每个数组元素加到结果集中。
通过loadFactoryNames方法只是获取到了实现类的类名,这些实现类并未初始化。初始化工作由createSpringFactoriesInstances方法实现。
在这里插入图片描述

在这里插入图片描述
接口中首先通过 ClassUtils.forName加载实现类的class字节码,然后通过Assert.isAssignable检查改实现类是否真正实现type接口,最后通过反射方法创建该实现类。
创建初始化器和创建监听器都是通过这种机制创建ApplicationContextInitializer实现类和ApplicationListener实现类,并将这些实现类存到变量initializers和listeners。

确定主配置类

在这里插入图片描述
源码很简单,从 deduceMainApplicationClass 方法开始往上爬,哪一层调用栈上有main方法,方法对应的类就是主配置类,就返回这个类。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值