Spring注解之@Import注解的使用和源碼分析
介紹
今天主要介紹Spring @Import注解,在Spring中@Import使用得比較頻繁,它得作用是導(dǎo)入bean,具體的導(dǎo)入方式有多種,特別在SpringBoot項目中,很多地方都使用到了@Import注解,特別對于一些和SpringBoot整合的組件,其實現(xiàn)都大量使用了@Import,例如使用Feign集成SpringBoot時會加上注解@EnableFeignClients,使用Dubbo時會使用@EnableDubbo等,這些注解里面都使用了@Import注解來注冊一些bean。
@Import導(dǎo)入bean的三種方式
@Import導(dǎo)入bean有三種方式,分別是導(dǎo)入普通類,實現(xiàn)ImportSelector接口的類,實現(xiàn)ImportBeanDefinitionRegistrar接口的類。
普通類
在開放過程中,盡量保持類不要太過于龐大,類過于龐大的話會變得臃腫復(fù)雜,不好維護,一個配置類中需要配置很多bean,且邏輯實現(xiàn)也比較復(fù)雜,代碼量大,如果全部都放在同一個配置類中,這顯然不太理智,這時候我們可以將每個bean單獨拿出來放到一個類里面,然后使用@Import注解導(dǎo)入,如下代碼所示。
- 定義一個bean
@Data
public?class?UserBean?{
????private?String?username;
????private?String?sex;
}- 導(dǎo)入bean
@Configuration
@Import(value?=?{UserBean.class})??//注入普通Bean
public?class?ImportConfiguration?{
}從上面可以看出只需要在配置類上面使用@Import注解導(dǎo)入對應(yīng)Java Bean,然后這個bean就能注冊進IOC容器中。
ImportSelector接口
ImportSelector是一個接口,可以通過實現(xiàn)它來完成bean的注冊,它只有一個selectImports()方法,它會返回一個bean的名稱數(shù)組,這個數(shù)組中的bean名稱就會被注冊進IOC容器中。
public?class?MyImportSelector?implements?ImportSelector?{
????@Override
????public?String[]?selectImports(AnnotationMetadata?importingClassMetadata)?{
????????return?new?String[]{UserBean.class.getName()};
????}
}ImportBeanDefinitionRegistrar接口
使用ImportBeanDefinitionRegistrar也可以注冊bean,它會傳入BeanDefinitionRegistry接口,然后進可以注冊bean,這里注冊的是bean的元信息BeanDefinition。
public?class?MyImportBeanDefinitionRegistrar?implements?ImportBeanDefinitionRegistrar?{
????@Override
????public?void?registerBeanDefinitions(AnnotationMetadata?importingClassMetadata,?BeanDefinitionRegistry?registry)?{
????????String?name?=?UserBean.class.getName();
????????BeanDefinitionBuilder?builder?=?BeanDefinitionBuilder.rootBeanDefinition(UserBean.class);
????????builder.addPropertyValue("sex","男");
????????AbstractBeanDefinition?beanDefinition?=?builder.getBeanDefinition();
????????registry.registerBeanDefinition(name,?beanDefinition);
????}
}源碼解析
spring容器啟動后,會在ConfigurationClassParser解析類中解析@Import注解,解析出需要注冊的bean,下面就是最關(guān)鍵的代碼,通過調(diào)用processImports方法,然后解析出對應(yīng)的bean,可以看出有幾個判斷,分別判斷是否是ImportSelector類型,ImportBeanDefinitionRegistrar類型,如果都不是,則證明是直接導(dǎo)入普通java類,如果是普通java類和ImportSelector類型,那么就會將要注冊的bean加入一個Map集合configurationClasses中,后續(xù)會將它進行注冊,如果是ImportBeanDefinitionRegistrar類型,那么會將其加入一個Map集合importBeanDefinitionRegistrars中,后續(xù)在擴展點會對它進行再次處理。
private?void?processImports(ConfigurationClass?configClass,?ConfigurationClassParser.SourceClass?currentSourceClass,
????????????????????????????????Collection<ConfigurationClassParser.SourceClass>?importCandidates,?Predicate<String>?exclusionFilter,
????????????????????????????????boolean?checkForCircularImports)?{
????????if?(candidate.isAssignable(ImportSelector.class))?{
????????????Class<?>?candidateClass?=?candidate.loadClass();
????????????ImportSelector?selector?=?ParserStrategyUtils.instantiateClass(candidateClass,?ImportSelector.class,
????????????????????this.environment,?this.resourceLoader,?this.registry);
????????????Predicate<String>?selectorFilter?=?selector.getExclusionFilter();
????????????if?(selectorFilter?!=?null)?{
????????????????exclusionFilter?=?exclusionFilter.or(selectorFilter);
????????????}
????????????if?(selector?instanceof?DeferredImportSelector?deferredImportSelector)?{
????????????????this.deferredImportSelectorHandler.handle(configClass,?deferredImportSelector);
????????????}?else?{
????????????????String[]?importClassNames?=?selector.selectImports(currentSourceClass.getMetadata());
????????????????Collection<ConfigurationClassParser.SourceClass>?importSourceClasses?=?asSourceClasses(importClassNames,?exclusionFilter);
????????????????processImports(configClass,?currentSourceClass,?importSourceClasses,?exclusionFilter,?false);
????????????}
????????}?else?if?(candidate.isAssignable(ImportBeanDefinitionRegistrar.class))?{
????????????Class<?>?candidateClass?=?candidate.loadClass();
????????????ImportBeanDefinitionRegistrar?registrar?=
????????????????????ParserStrategyUtils.instantiateClass(candidateClass,?ImportBeanDefinitionRegistrar.class,
????????????????????????????this.environment,?this.resourceLoader,?this.registry);
????????????configClass.addImportBeanDefinitionRegistrar(registrar,?currentSourceClass.getMetadata());
????????}?else?{
????????????this.importStack.registerImport(
????????????????????currentSourceClass.getMetadata(),?candidate.getMetadata().getClassName());
????????????processConfigurationClass(candidate.asConfigClass(configClass),?exclusionFilter);
????????}
????}經(jīng)過上面解析后,Spring會注冊Bean的元信息,會通過configClass.isImported()判斷bean是否是通過@Import方式導(dǎo)入的普通bean或者ImportSelector類型的導(dǎo)入的bean,如果是,則執(zhí)行registerBeanDefinitionForImportedConfigurationClass,里面主要就是組裝成BeanDefinition,然后注冊進BeanFactory。
private?void?loadBeanDefinitionsForConfigurationClass(ConfigurationClass?configClass,?ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator?trackedConditionEvaluator)?{
????????if?(trackedConditionEvaluator.shouldSkip(configClass))?{
????????????String?beanName?=?configClass.getBeanName();
????????????if?(StringUtils.hasLength(beanName)?&&?this.registry.containsBeanDefinition(beanName))?{
????????????????this.registry.removeBeanDefinition(beanName);
????????????}
????????????this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
????????????return;
????????}
????????if?(configClass.isImported())?{
????????????registerBeanDefinitionForImportedConfigurationClass(configClass);
????????}
????????for?(BeanMethod?beanMethod?:?configClass.getBeanMethods())?{
????????????loadBeanDefinitionsForBeanMethod(beanMethod);
????????}
????????loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
????????loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
????}如果是通過ImportBeanDefinitionRegistrar方式,則會調(diào)用loadBeanDefinitionsFromRegistrars,里面會循環(huán)去執(zhí)行我們自定義的ImportBeanDefinitionRegistrar,然后進行bean的元信息注冊。
private?void?loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar,?AnnotationMetadata>?registrars)?{
????registrars.forEach((registrar,?metadata)?->
????????registrar.registerBeanDefinitions(metadata,?this.registry,?this.importBeanNameGenerator));
?}從上面的源碼解析中,我們看出通過@Import直接導(dǎo)入普通的java類和導(dǎo)入實現(xiàn)了ImportSelector接口的類是直接注冊進BeanFactory,這兩者本質(zhì)是一樣的,而通過實現(xiàn)ImportBeanDefinitionRegistrar接口方式的類則需要去實現(xiàn)我們自定義的注冊bean元信息的邏輯。
總結(jié)
上面我們介紹了@Import的一些場景,@Import用得最多還是一些和Spring結(jié)合的中間件里面,也介紹了它的幾種使用方式,還對它的源碼進行解析,當(dāng)然,只是從它最主要的邏輯去分析,深入的邏輯就沒去一一詳解,掌握@Import有助于我們在使用一些其他框架的時候能夠了解框架的實現(xiàn)原理,然后更好的去使用框架!
以上就是Spring注解之@Import注解的使用和源碼分析的詳細(xì)內(nèi)容,更多關(guān)于Spring注解@Import的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringMVC通過模型視圖ModelAndView渲染視圖的實現(xiàn)
這篇文章主要介紹了SpringMVC通過模型視圖ModelAndView渲染視圖的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
SpringBoot如何基于POI-tl和word模板導(dǎo)出龐大的Word文件
這篇文章主要介紹了SpringBoot如何基于POI-tl和word模板導(dǎo)出龐大的Word文件,poi-tl是一個基于Apache?POI的Word模板引擎,也是一個免費開源的Java類庫2022-08-08
Java 8 動態(tài)類型語言Lambda表達式實現(xiàn)原理解析
Java 8支持動態(tài)語言,看到了很酷的Lambda表達式,對一直以靜態(tài)類型語言自居的Java,讓人看到了Java虛擬機可以支持動態(tài)語言的目標(biāo)。接下來通過本文給大家介紹Java 8 動態(tài)類型語言Lambda表達式實現(xiàn)原理分析,需要的朋友可以參考下2017-02-02
Java實現(xiàn)發(fā)送手機短信語音驗證功能代碼實例
這篇文章主要介紹了Java實現(xiàn)發(fā)送手機短信語音驗證功能代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-09-09
使用dynamic datasource springboot starter實現(xiàn)多數(shù)據(jù)源及源碼分析
這篇文章主要介紹了使用dynamic-datasource-spring-boot-starter做多數(shù)據(jù)源及源碼分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
Java?對象在?JVM?中的內(nèi)存布局超詳細(xì)解說
這篇文章主要介紹了Java?對象在?JVM?中的內(nèi)存布局超詳細(xì)解說,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09
java實現(xiàn)兩張圖片2D翻轉(zhuǎn)動畫效果
這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)兩張圖片2D翻轉(zhuǎn)動畫效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08

