SpringBoot自动装配原理分析,手写starter组件

我们就从 SpringBoot的这个注解开始入手,看看这个注解到底替我们做了什么。

前面四个不用说,是定义一个注解所必须的,关键就在于后面三个注解:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan。也就是说我们如果不用 @SpringBootApplication 这个复合注解,而是直接使用最下面这三个注解,也能启动一个 SpringBoot 应用。

@SpringBootConfiguration 注解

这个注解我们点进去就可以发现,它实际上就是一个 @Configuration 注解,这个注解大家应该很熟悉了,加上这个注解就是为了让当前类作为一个配置类交由 Spring 的 IOC 容器进行管理,因为前面我们说了,SpringBoot 本质上还是 Spring,所以原属于 Spring 的注解 @Configuration 在 SpringBoot 中也可以直接应用。

@ComponentScan 注解

这个注解也很熟悉,用于定义 Spring 的扫描路径,等价于在 xml 文件中配置 context:component-scan,假如不配置扫描路径,那么 Spring 就会默认扫描当前类所在的包及其子包中的所有标注了 @Component,@Service,@Controller 等注解的类。

@EnableAutoConfiguration

这个注解才是实现自动装配的关键,点进去之后发现,它是一个由 @AutoConfigurationPackage 和 @Import 注解组成的复合注解。

@EnableXXX 注解也并不是 SpringBoot 中的新注解,这种注解在 Spring 3.1 版本就开始出现了,比如开启定时任务的注解 @EnableScheduling 等。

@Import 注解

这个注解比较关键,我们通过一个例子来说明一下。

定义一个普通类 TestImport,不加任何注解,我们知道这个时候这个类并不会被 Spring 扫描到,也就是无法直接注入这个类:

public class TestImport {
}

现实开发中,假如就有这种情况,定义好了一个类,即使加上了注解,也不能保证这个类一定被 Spring 扫描到,这个时候该怎么做呢?

这时候我们可以再定义一个类 MyConfiguration,保证这个类可以被 Spring 扫描到,然后通过加上 @Import 注解来导入 TestImport 类,这时候就可以直接注入 TestImport 了:

@Configuration
@Import(TestImport.class)
public class MyConfiguration {
}

所以这里的 @Import 注解其实就是为了去导入一个类 
AutoConfigurationImportSelector,接下来我们需要分析一下这个类。

AutoConfigurationImportSelector 类

进入这个类之后,有一个方法,这个方法很好理解,首先就是看一下 AnnotationMetadata(注解的元信息),有没有数据,没有就说明没导入直接返回一个空数组,否则就调用 getAutoConfigurationEntry 方法:

进入 getAutoConfigurationEntry 方法:

这个方法里面就是通过调用 
getCandidateConfigurations 来获取候选的 Bean,并将其存为一个集合,最后经过去重,校验等一系列操作之后,被封装成 AutoConfigurationEntry 对象返回。

继续进入 
getCandidateConfigurations 方法,这时候就几乎看到曙光了:

这里面再继续点击去就没必要了,看错误提示大概就知道了,loadFactoryNames 方法会去 META-INF/spring.factories 文件中根据 EnableAutoConfiguration 的全限定类名获取到我们需要导入的类,而 EnableAutoConfiguration 类的全限定类名为 org.springframework.boot.autoconfigure.EnableAutoConfiguration,那么就让我们打开这个文件看一下:

可以看到,这个文件中配置了大量的需要自动装配的类,当我们启动 SpringBoot 项目的时候,SpringBoot 会扫描所有 jar 包下面的 META-INF/spring.factories 文件,并根据 key 值进行读取,最后在经过去重等一些列操作得到了需要自动装配的类。

需要注意的是:上图中的 spring.factories 文件是在 spring-boot-autoconfigure 包下面,这个包记录了官方提供的 stater 中几乎所有需要的自动装配类,所以并不是每一个官方的 starter 下都会有 spring.factories 文件。

谈谈 SPI 机制

通过 SpringFactoriesLoader 来读取配置文件 spring.factories 中的配置文件的这种方式是一种 SPI 的思想。那么什么是 SPI 呢?

SPI,Service Provider Interface。即:接口服务的提供者。就是说我们应该面向接口(抽象)编程,而不是面向具体的实现来编程,这样一旦我们需要切换到当前接口的其他实现就无需修改代码。

在 Java 中,数据库驱动就使用到了 SPI 技术,每次我们只需要引入数据库驱动就能被加载的原因就是因为使用了 SPI 技术。

打开 DriverManager 类,其初始化驱动的代码如下:

进入 ServiceLoader 方法,发现其内部定义了一个变量:

private static final String PREFIX = “META-INF/services/”;

这个变量在下面加载驱动的时候有用到,下图中的 service 即 java.sql.Driver:

所以就是说,在数据库驱动的 jar 包下面的 META-INF/services/ 下有一个文件 java.sql.Driver,里面记录了当前需要加载的驱动,我们打开这个文件可以看到里面记录的就是驱动的全限定类名:

@AutoConfigurationPackage 注解

从这个注解继续点进去之后可以发现,它最终还是一个 @Import 注解:

这个时候它导入了一个 AutoConfigurationPackages 的内部类 Registrar, 而这个类其实作用就是读取到我们在最外层的 @SpringBootApplication 注解中配置的扫描路径(没有配置则默认当前包下),然后把扫描路径下面的类都加到数组中返回。

手写一个 stater 组件

了解完自动装配的原理,接下来就可以动手写一个自己的 starter 组件了。

starter 组件命名规则

SpringBoot 官方的建议是,如果是我们开发者自己开发的 starter 组件(即属于第三方组件),那么命名规范是{name}-spring-boot-starter,而如果是 SpringBoot 官方自己开发的组件,则命名为 spring-boot-starter-{name}`。

当然,这只是一个建议,如果非不按这个规则也没什么问题,但是为了更好的识别区分,还是建议按照这个规则来命名。

手写 starter

写一个非常简单的组件,这个组件只做一件事,那就是实现 fastjson 序列化。

  • 新建一个 SpringBoot 应用 lonelyWolf-spring-boot-starter。
  • 修改 pom 文件,并新增 fastjson 依赖(省略了部分属性)。
org.springframework.boot spring-boot-starter-parent 2.4.0

com.lonely.wolf.note
lonelyWolf-spring-boot-starter
1.0.0-SNAPSHOT

org.springframework.boot spring-boot-starter com.alibaba fastjson 1.2.72
  • 新建一个序列化类 JsonSerial 类来实现 fastjson 序列化。

public class JsonSerial {
public String serial(T t){
return JSONObject.toJSONString(t);
}
}

  • 新建一个自动装配类 MyAutoConfiguration 来生成 JsonSerial。

@Configuration
public class MyAutoConfiguration {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

结尾

查漏补缺:Java岗 千+道面试题Java基础+全家桶+容器+反射+异常等

这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。

由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!**
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值