javascript
Spring Boot - 自动装配中的不可忽视的@Import
文章目錄
- Pre
- 四種處理方式
- 從@SpringBootApplication注解說起
- @SpringBootApplication組合注解說明
- @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited
- @SpringBootCon?guration
- @EnableAutoConfiguration
- @ComponentScan
- @EnableAutoConfiguration
- @AutoConfigurationPackage
- @Import(AutoConfigurationImportSelector.class)
- @Import源碼
- @Import 普通組件
- @Import 實(shí)現(xiàn)了ImportSelector接口的組件 (類的全限定類名)
- @Import 實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口的組件
- 最佳實(shí)踐 @EnableXXX
- 【自定義注解 @EnableArtisan】
- 將AppConfig.java添加@EnableArtisan
- 源碼
Pre
Spring Boot - 自動(dòng)配置實(shí)現(xiàn)原理
四種處理方式
在使用 Spring Boot 時(shí),@Import 也是一個(gè)非常常見的注解,可以用來動(dòng)態(tài)創(chuàng)建 Bean。
在 @Import 注解的屬性中可以設(shè)置需要引入的類名,例如 @AutoConfigurationPackage 注解上的 @Import(AutoConfigurationPackages.Registrar.class)。根據(jù)該類的不同類型,Spring 容器針對 @Import 注解有以下四種處理方式:
- 如果該類實(shí)現(xiàn)了 ImportSelector 接口,Spring 容器就會(huì)實(shí)例化該類,并且調(diào)用其 selectImports 方法;
- 如果該類實(shí)現(xiàn)了 DeferredImportSelector 接口,則 Spring 容器也會(huì)實(shí)例化該類并調(diào)用其 selectImports方法。DeferredImportSelector 繼承了 ImportSelector,區(qū)別在于 DeferredImportSelector 實(shí)例的 selectImports 方法調(diào)用時(shí)機(jī)晚于 ImportSelector 的實(shí)例,要等到 @Configuration 注解中相關(guān)的業(yè)務(wù)全部都處理完了才會(huì)調(diào)用;
- 如果該類實(shí)現(xiàn)了 ImportBeanDefinitionRegistrar 接口,Spring 容器就會(huì)實(shí)例化該類,并且調(diào)用其 registerBeanDefinitions 方法;
- 如果該類沒有實(shí)現(xiàn)上述三種接口中的任何一個(gè),Spring 容器就會(huì)直接實(shí)例化該類。
我們來搞一搞@Import吧
從@SpringBootApplication注解說起
我們都知道 啟動(dòng)一個(gè)SpringBoot應(yīng)用 無須各種的配置文件,無須各種繁雜的pom依賴,一個(gè)main方法,就能run起來了。
與其他框架整合也相當(dāng)方便,使用EnableXXXXX注解就可以完成整合
那SpringBoot是如何實(shí)現(xiàn)自動(dòng)配置的????
@SpringBootApplication組合注解說明
@SpringBootApplication: Spring Boot應(yīng)用標(biāo)注在某個(gè)類上說明這個(gè)類是SpringBoot的主配置類,SpringBoot需要運(yùn)行這個(gè)類的main方法來啟動(dòng)SpringBoot應(yīng)用
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited
注解說明:
- @Target(ElementType.TYPE) 設(shè)置當(dāng)前注解可以標(biāo)記在哪
- @Retention(RetentionPolicy.RUNTIME) 當(dāng)注解標(biāo)注的類編譯以什么方式保留。 RetentionPolicy.RUNTIME 會(huì)被jvm加載
- @Documented java doc 會(huì)生成注解信息
- @Inherited 是否會(huì)被繼承
更詳細(xì)的請參考我以前寫的一篇博文: Java-Java5.0注解解讀
@SpringBootCon?guration
Spring Boot的配置類 , 標(biāo)注在某個(gè)類上,表示這是一個(gè)Spring Boot的配置類
@EnableAutoConfiguration
開啟自動(dòng)配置功能 , @EnableAutoCon?guration告訴SpringBoot開啟自動(dòng)配置,會(huì)自動(dòng)去加載自動(dòng)配置類
@ComponentScan
相當(dāng)于在spring.xml 配置中<context:comonent-scan> 但是并沒有指定basepackage,如果沒有指定spring底層會(huì)自動(dòng)掃描當(dāng)前配置類所有在的包
@EnableAutoConfiguration
SpringBootApplication注解中最重要的一個(gè)注解就是 @EnableAutoConfiguration.
@AutoConfigurationPackage
將當(dāng)前配置類所在包保存在BasePackages的Bean中。供Spring內(nèi)部使用
使用了@Import注解 保存掃描路徑, 注冊
那看下 org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar
@Import(AutoConfigurationImportSelector.class)
關(guān)鍵點(diǎn)!
可以看到,在@EnableAutoConfiguration注解內(nèi)也使用到了@Import注解來完成導(dǎo)入配置的功能
那都用它,我們來搞搞@Import吧
@Import源碼
@Import表示要導(dǎo)入的一個(gè)或多個(gè)@Configuration類
我們來看下value方法源碼中的注釋: Configuration,ImportSelector,ImportBeanDefinitionRegistrar 或者是一個(gè)普通的組件
那分別來看下如何使用吧
POM 核心 有個(gè)context就夠了
<dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.18</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.6.RELEASE</version></dependency></dependencies>@Import 普通組件
@Import({ 要導(dǎo)入的組件 } )
【模擬第三方框架提供的】
package com.artisan.configuration;import com.artisan.beans.Artisan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;/*** @author 小工匠* @version 1.0* @description: 自定義的配置類 類比第三方的配置類* @date 2021/5/22 8:50* @mark: show me the code , change the world*/@Configuration public class ArtisanConfig {@Beanpublic Artisan artisan() {Artisan artisan = new Artisan();artisan.setName("小工匠");artisan.setAge(18);return artisan;} }【模擬Spring自己的】
/*** 系統(tǒng)當(dāng)前加載的配置類*/@Configuration @Import({ArtisanConfig.class}) // @Import(ArtisanImportSelector.class) //@Import(ArtisanRegistrar.class) //@EnableArtisan public class AppConfig { }將AppConfig.java添加注解 @Import({ArtisanConfig.class}) , 將第三方的配置類導(dǎo)入到Bean容器中 , 本質(zhì)上就是導(dǎo)入 一個(gè)Configuration配置類組件
【測試類】
public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);Artisan artisan = ctx.getBean(Artisan.class);System.out.println(artisan);} }【測試結(jié)果】
@Import 實(shí)現(xiàn)了ImportSelector接口的組件 (類的全限定類名)
【ImportSelector接口 返回全限定名】
package com.artisan.impt;import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/5/22 9:25* @mark: show me the code , change the world*/ public class ArtisanImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{"com.artisan.beans.Artisan"};} }【AppConfig - @Import(ArtisanImportSelector.class)】
/*** 系統(tǒng)當(dāng)前加載的配置類*/@Configuration //@Import({ArtisanConfig.class})@Import(ArtisanImportSelector.class) //@Import(ArtisanRegistrar.class) //@EnableArtisan public class AppConfig { }測試
ArtisanSelector返回的類的全限定類名,即為導(dǎo)入到容器中的組件全類名
@Import 實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口的組件
【ImportBeanDefinitionRegistrar 接口】
package com.artisan.impt;import com.artisan.beans.Artisan; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/5/22 9:28* @mark: show me the code , change the world*/ public class ArtisanRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Artisan.class);builder.setScope(BeanDefinition.SCOPE_SINGLETON);builder.addPropertyValue("name", "小工匠Registrar");builder.addPropertyValue("age", "18");registry.registerBeanDefinition("artisan", builder.getBeanDefinition());} }【AppConfig - @Import(ArtisanRegistrar.class) 】
/*** 系統(tǒng)當(dāng)前加載的配置類*/@Configuration //@Import({ArtisanConfig.class}) // @Import(ArtisanImportSelector.class) @Import(ArtisanRegistrar.class) //@EnableArtisan public class AppConfig { }【測試】
ImportBeanDefinitionRegistrar類似于ImportSelector用法,只不過這種用法能自定義化注冊,往容器內(nèi)注入一個(gè)BeanDefinition,然后BeanDeiniton在容器內(nèi)轉(zhuǎn)為一個(gè)實(shí)例bean。
最佳實(shí)踐 @EnableXXX
【自定義注解 @EnableArtisan】
package com.artisan.annotation;import com.artisan.configuration.ArtisanConfig; import com.artisan.impt.ArtisanImportSelector; import com.artisan.impt.ArtisanRegistrar; import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented// @Import({ArtisanConfig.class}) @Import({ArtisanImportSelector.class}) 都可以 @Import({ArtisanRegistrar.class}) public @interface EnableArtisan {}將AppConfig.java添加@EnableArtisan
【測試結(jié)果】
源碼
https://github.com/yangshangwei/boot2/tree/master/spring_maven
總結(jié)
以上是生活随笔為你收集整理的Spring Boot - 自动装配中的不可忽视的@Import的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring - BeanDefinit
- 下一篇: Spring Boot - 自动配置实例