文章目录
前言
我们知道使用
springboot
构建一个应用非常简单,在引入以来后,只需要编写一个SpringBoot
的主启动类就可以运行SpringBoot
应用,这里主启动类上必须标注@SpringBootApplication
注解,接下来我们会剖析其内部实现原理
1、SpringBootApplication注解剖析
从下图可以看出来他是一个组合注解,等价于同时标注
@Configuration
+@EnableAutoConfiguration
+@ComponentScan
注解。
@ComponentScan
作用:可以指定包扫描的跟路径,不指定路径默认扫描当前配置类所在包及其子包里的所有组件@SpringBootApplication
作用:这个注解是被@Configration
注解标注,说明他其实是用来标注配置类的,而且是标注主启动类的。@EnableAutoConfiguration
作用:这个注解作用比较复杂,下面单独做分析。
1.1、@EnableAutoConfiguration
在这里分析注解的时候先补充一些前置逻辑,在spring中,装配组件的方式有三种:
- 使用模块注解
@Component
以及其延伸注解标注- 使用配置类@
Configuration
与@Bean
注解标注- 使用模块装配
@Enablexxx
和@Import
注解标注
1.1.1、模块装配
前两种装配方式我们很熟悉,这里模块装配在平常开发中不常用,但是在很多集成的框架源码中非常常用,我们先看下模块装配使用示例。
- 使用
@import
导入普通类
- 使用
@import
导入配置类
- 使用
@import
导入ImportSelector
,这里可以通过实现ImportSelector
接口,在实现方法中添加要注册进容器中的类的名字。
- 导入
ImportBeanDefinitionRegistrar
,这里可以通过实现ImportBeanDefinitionRegistrar
接口,在实现方法中自己构建BeanDefinition
注册到容器中。
1.1.2、@EnableAutoConfiguration注解解析
- 这个注解也是一个组合注解,首先顶着一个
@AutoConfigurationPackage
注解,
然后使用@Import
导入了一个AutoConfigurationImportSelector
类,下面我们来剖析一下。
@AutoConfigurationPackage
,这个注解在内部又使用了@Import
注解导入了Registrar
这个类
Registrar
这个类向容器中保存了导入的配置类所在的包的根路径,这个配置类也就是我们的主启动类,这个是用来后期扫描组件使用的。
@Import({AutoConfigurationImportSelector.class})
导入了AutoConfigurationImportSelector
类。
- 这里发现他没有直接实现
ImportSelector
而是实现DeferredImportSelector
,这个是因为DeferredImportSelector
的执行实际比ImportSelector
要持,这种对需要标注@Conditional
的导入来说很有用。
在
selectImports
方法中会调用getAutoConfigurationEntry
加载自动配置类,这里重点就是调用getCandidateConfigurations
方法获取到一个配置列表,然后把这些配置返回,通过selectImport
装配到容器中,那这个返回的必然是其他组件的核心。
getCandidateConfigurations
方法内部又调用了SpringFactoriesLoader#leadFactoryNames
,传入的Class
就是@EnableAutoConfiguration
。
SpringFactoriesLoader#loadFactoryNames
方法中会使用classLoader
取加载指定路径下的资源,这个路径是META-INF/spring.factories
。
我们看下
spring-boot-autoconfiguration
包下META-INF/spring.factories
,在这个文件中全是自动装配类的全限定类名,装配到IOC
容器中,之后自动配置类就会通过ImportSelector
的机制被创建出来。
2、SPI机制
SPI
全称为Service Provider Interface
,是jdk
内置的一种服务提供发现机制。简单来说,它就是一种动态替换发现的机制。SPI
规定,所有要预先声明的类都应该放在META-INF/services
中。配置的文件名是接口/抽象类的全限定名,文件内容是抽象类的子类或接口的实现类的全限定类名,如果有多个,借助换行符,一行一个。
我们在上面可以看到
Spring
是通过SPI
机制来进行服务发现的,但是使用的是SpringFactoriesLoader
而没有使用java``的ServiceLeader
来进行加载,我们来剖析一下SpringFactoriesLoader
,看和java
原生的ServiceLeader
的区别:
- 之前看过
spring.factories
文件,文件中定义的是key-value
的关系,这样设计的好处就不再局限于接口-实现类的关系,key
可以随意定义,比如是一个注解。- 从下图的代码中可以看出,加载完成之后会把
result
放到cache
中,这时候下次再加载的时候就就可以直接从缓存中获取了,无需重复加载。
总结
- 通过
AutoConfigurationImportSelector
配置SpringFactoriesLoader
可以加载路径“META-INF/spring.factories
”文件中配置的许多自动装配类。Spring
自己实现了SPI
技术,使用key-value
更灵活,增加了缓存避免重复加载耗费资源。