springboot的Spi机制
在与Springboot
打交道的过程中,应该大家对starter
都不会陌生,springboot
中的各种starter
其实就是利用了springboot
的SPI
机制。
在springboot
中,会默认扫描Applicatoin
启用类及其子包里面的配置类Bean
(比如标记@Configuration
,@Component
,@Server
等)然后进行初始化,那么如果你是为别人提供二方包,三方包的库,如果你的需要加载的类路径跟他的不一样,那么根据Springboot
的bean
加载机制是不会加载到的。
主工程:
1 | ├── src |
二方库A:
1 | ├── src |
二方库B:
1 | ├── src |
这时只有二方库A可以正确加载MybatisXmlReloadConfig
这个bean
,因为它的路径在主工程的Application
的包以及子包下,那么就出现所有的二方库、三方库都需要与主工程的类路径一致?
这里Springboot
就提供了Spi
的机制用来获取二方库,三方库中需要加载的Bean
对象。
注意
:一定要确保二方库、三方库的类路径不在主工程Application
下的情况,在考虑SPI
逻辑,我之前就掉入一个陷阱,因为我的二方库的类路径与主工程一致,但是那个时候我的spring.factories
指向的路径是错的,导致一度怀疑自己理解的SPI
有出入。
二方库、三方库类路径与主工程启动类路径不一致的情况下!!!(再次强调!)如果使得主工程可以加载到对应库中的bean
对象。
创建
META-INF/spring.factories
文件。这个文件中是k-v
的结构,一个key
对应多个逗号分隔的value
。对于这个文件来说,可以配置的key
有很多,比如有EnableAutoConfiguration
,ApplicationContextInitializer
,ApplicationListener
1
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xiaocainiaoya.MyImportTest
在主工程启动时,会加载
META-INF/spring.factories
文件,对这个文件内指向的bean
对象进行逐个加载。
主要源码在AutoConfigurationImportSelector#getCandidateConfigurations
中。
1 | protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { |
注意:在Springboot2.7
之后引入了新的SPI
配置方式。
创建
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件。这个文件的文件名是固定,文件内容是value
,各个value
之间通过回车分隔。1
com.bosssoft.xiaocainiaoya.MyImportTest
主要源码同样是在AutoConfigurationImportSelector#getCandidateConfigurations
中进行了兼容。
1 | protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { |
@AutoConfiguration
在Springboot
中可以直接使用@AutoConfiguration
来启用SPI
机制,这是一个组合注解,通过@AliasFor
注解将值传递给@Configuration
、@AutoConfigureBefore
、@AutoConfigureAfter
三个注解。
注:使用这个注解需要配合META-INF/spring
中设置org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中配置全限定类名。
1 |
|
org.springframework.boot.autoconfigure.AutoConfiguration.imports
1 | cn.com.xiaocainiaoya.config.MyAutoConfiguration |
注:我之前进入一个误区,以为组合注解标记在某个类上就表明这个类拥有组合注解中的相关能力,其实这是错误的。
1 | /** |
这里我创建了类MyAutoConfiguration
,标记了@AutoConfiguration
注解,虽然这个注解是组合注解,也就是说实际上这个类也被@AutoConfiguration
注解中的@Configuration
标记,但是启动之后发现,在没有写META-INF/spring
文件的情况下是不会加载,所以这里要解释一下,注解仅仅是标记了某个类,注解要起到什么作用取决于获取这个注解的动作的代码逻辑是如何。换句话说,标记了@AutoConfiguration
实际上是可以具备@Configuration
能力(不写META-INF/spring
文件就可以被加载到容器中),但是获取这个注解的处理端可以选择不赋予这种能力。感觉还是没有说情况,文字表述不能很清楚的表达,上代码!
1 | // 当扫描到MyAutoConfiguration类需要进行容器化处理时肯定是需要进入某个处理器来进行处理 |