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,ApplicationListener1
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类需要进行容器化处理时肯定是需要进入某个处理器来进行处理 |