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 實現了ImportSelector接口的組件 (類的全限定類名)
- @Import 實現了ImportBeanDefinitionRegistrar接口的組件
- 最佳實踐 @EnableXXX
- 【自定義注解 @EnableArtisan】
- 將AppConfig.java添加@EnableArtisan
- 源碼
Pre
Spring Boot - 自動配置實現原理
四種處理方式
在使用 Spring Boot 時,@Import 也是一個非常常見的注解,可以用來動態創建 Bean。
在 @Import 注解的屬性中可以設置需要引入的類名,例如 @AutoConfigurationPackage 注解上的 @Import(AutoConfigurationPackages.Registrar.class)。根據該類的不同類型,Spring 容器針對 @Import 注解有以下四種處理方式:
- 如果該類實現了 ImportSelector 接口,Spring 容器就會實例化該類,并且調用其 selectImports 方法;
- 如果該類實現了 DeferredImportSelector 接口,則 Spring 容器也會實例化該類并調用其 selectImports方法。DeferredImportSelector 繼承了 ImportSelector,區別在于 DeferredImportSelector 實例的 selectImports 方法調用時機晚于 ImportSelector 的實例,要等到 @Configuration 注解中相關的業務全部都處理完了才會調用;
- 如果該類實現了 ImportBeanDefinitionRegistrar 接口,Spring 容器就會實例化該類,并且調用其 registerBeanDefinitions 方法;
- 如果該類沒有實現上述三種接口中的任何一個,Spring 容器就會直接實例化該類。
我們來搞一搞@Import吧
從@SpringBootApplication注解說起
我們都知道 啟動一個SpringBoot應用 無須各種的配置文件,無須各種繁雜的pom依賴,一個main方法,就能run起來了。
與其他框架整合也相當方便,使用EnableXXXXX注解就可以完成整合
那SpringBoot是如何實現自動配置的????
@SpringBootApplication組合注解說明
@SpringBootApplication: Spring Boot應用標注在某個類上說明這個類是SpringBoot的主配置類,SpringBoot需要運行這個類的main方法來啟動SpringBoot應用
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited
注解說明:
- @Target(ElementType.TYPE) 設置當前注解可以標記在哪
- @Retention(RetentionPolicy.RUNTIME) 當注解標注的類編譯以什么方式保留。 RetentionPolicy.RUNTIME 會被jvm加載
- @Documented java doc 會生成注解信息
- @Inherited 是否會被繼承
更詳細的請參考我以前寫的一篇博文: Java-Java5.0注解解讀
@SpringBootCon?guration
Spring Boot的配置類 , 標注在某個類上,表示這是一個Spring Boot的配置類
@EnableAutoConfiguration
開啟自動配置功能 , @EnableAutoCon?guration告訴SpringBoot開啟自動配置,會自動去加載自動配置類
@ComponentScan
相當于在spring.xml 配置中<context:comonent-scan> 但是并沒有指定basepackage,如果沒有指定spring底層會自動掃描當前配置類所有在的包
@EnableAutoConfiguration
SpringBootApplication注解中最重要的一個注解就是 @EnableAutoConfiguration.
@AutoConfigurationPackage
將當前配置類所在包保存在BasePackages的Bean中。供Spring內部使用
使用了@Import注解 保存掃描路徑, 注冊
那看下 org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar
@Import(AutoConfigurationImportSelector.class)
關鍵點!
可以看到,在@EnableAutoConfiguration注解內也使用到了@Import注解來完成導入配置的功能
那都用它,我們來搞搞@Import吧
@Import源碼
@Import表示要導入的一個或多個@Configuration類
我們來看下value方法源碼中的注釋: Configuration,ImportSelector,ImportBeanDefinitionRegistrar 或者是一個普通的組件
那分別來看下如何使用吧
POM 核心 有個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({ 要導入的組件 } )
【模擬第三方框架提供的】
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自己的】
/*** 系統當前加載的配置類*/@Configuration @Import({ArtisanConfig.class}) // @Import(ArtisanImportSelector.class) //@Import(ArtisanRegistrar.class) //@EnableArtisan public class AppConfig { }將AppConfig.java添加注解 @Import({ArtisanConfig.class}) , 將第三方的配置類導入到Bean容器中 , 本質上就是導入 一個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);} }【測試結果】
@Import 實現了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)】
/*** 系統當前加載的配置類*/@Configuration //@Import({ArtisanConfig.class})@Import(ArtisanImportSelector.class) //@Import(ArtisanRegistrar.class) //@EnableArtisan public class AppConfig { }測試
ArtisanSelector返回的類的全限定類名,即為導入到容器中的組件全類名
@Import 實現了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) 】
/*** 系統當前加載的配置類*/@Configuration //@Import({ArtisanConfig.class}) // @Import(ArtisanImportSelector.class) @Import(ArtisanRegistrar.class) //@EnableArtisan public class AppConfig { }【測試】
ImportBeanDefinitionRegistrar類似于ImportSelector用法,只不過這種用法能自定義化注冊,往容器內注入一個BeanDefinition,然后BeanDeiniton在容器內轉為一個實例bean。
最佳實踐 @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
【測試結果】
源碼
https://github.com/yangshangwei/boot2/tree/master/spring_maven
總結
以上是生活随笔為你收集整理的Spring Boot - 自动装配中的不可忽视的@Import的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring - BeanDefinit
- 下一篇: Spring Boot - 自动配置实例