Spring注解之@Import使用方法講解
1. 前言
Spring提供了@Import
注解,用于向容器引入我們自定義的類,在許多注解中我們都會看到它的身影,比如MyBatis在整合Spring時,提供的@MapperScan
注解:
@Import(MapperScannerRegistrar.class) public @interface MapperScan { }
比如Spring開啟AOP功能的注解:
@Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { }
再比如Spring的異步調(diào)用注解:
@Import(AsyncConfigurationSelector.class) public @interface EnableAsync { }
大部分@Enable***
注解的原理都差不多,都是通過利用@Import
注解向Spring容器注入一些bean來實現(xiàn)的。
2. 用法
@Import
注解可以引入三種類。
1、引入普通類,將作為bean注冊到Spring容器。
@Configuration @Import(Person.class) public class Application { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); Person person = context.getBean(Person.class); } }
2、引入ImportSelector實現(xiàn)類,Spring會將**selectImports()**
方法返回的bean數(shù)組注冊到Spring容器,但是ImportSelector對象本身不會注冊到容器。
public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.javap.importt.Person"}; } }
3、引入ImportBeanDefinitionRegistrar實現(xiàn)類,Spring會暴露 BeanDefinitionRegistry對象,你可以自由的往容器里面注冊BeanDefinition,但是ImportBeanDefinitionRegistrar對象本身不會注冊到容器。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 往registry注冊BeanDefinition } }
MyBatis整合Spring時,提供的@MapperScan
注解引入的就是ImportBeanDefinitionRegistrar的實現(xiàn)類,MyBatis會去掃描指定的包路徑,然后將Mapper接口對應(yīng)的BeanDefinition注冊到容器里。
3. 源碼分析
解析類上的@Import
注解的職責(zé)Spring交給了ConfigurationClassPostProcessor類,它是BeanFactoryPostProcessor的子類,也就是說它屬于BeanFactory的擴展點之一,它會處理容器內(nèi)所有的ConfigurationClass。
ConfigurationClassPostProcessor首先會過濾出容器內(nèi)所有的ConfigurationClass,然后實例化一個ConfigurationClassParser解析器去解析ConfigurationClass,解析過程中就會處理類上的@Import
注解了,方法是ConfigurationClassParser#processImports()
。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // 引入的是ImportSelector子類 Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { // 調(diào)用子類方法,得到要引入的beanNames String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // 引入的是ImportBeanDefinitionRegistrar子類 Class<?> candidateClass = candidate.loadClass(); // 實例化子類對象 ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); // 暫存到Map,稍后觸發(fā) configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // 引入的是普通類,正常注入到容器 this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); // 引入的類可能是ConfigurationClass,遞歸處理 processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
方法做了三件事:
- 如果引入的是ImportSelector子類,實例化子類對象,調(diào)用
selectImports()
方法得到要注冊的beanNames,完成注冊。 - 如果引入的是ImportBeanDefinitionRegistrar子類,實例化子類對象,暫存到Map,稍后觸發(fā)。
- 如果引入的是普通類,正常注冊到容器。
對于ImportBeanDefinitionRegistrar子類,這里只是實例化了子類對象然后暫存到Map容器中,此時還沒有去觸發(fā)ImportBeanDefinitionRegistrar#registerBeanDefinitions()
方法。方法的觸發(fā)是在ConfigurationClass解析完以后,ConfigurationClassPostProcessor會調(diào)用ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars()
方法遍歷Map容器中所有的ImportBeanDefinitionRegistrar子類對象,挨個觸發(fā)擴展方法。
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry)); }
到此這篇關(guān)于Spring注解之@Import使用方法講解的文章就介紹到這了,更多相關(guān)Spring注解@Import內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容
這篇文章主要介紹了Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容的相關(guān)資料,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09一文詳解Spring事務(wù)的實現(xiàn)與本質(zhì)
這篇文章主要介紹了Spring中事務(wù)的兩種實現(xiàn)方式:聲明式事務(wù)、編程式事務(wù)以及他們的本質(zhì)。文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-04-04Spring如何利用@Value注解讀取yml中的map配置
這篇文章主要介紹了Spring如何利用@Value注解讀取yml中的map配置,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02詳解Spring Security 中的四種權(quán)限控制方式
這篇文章主要介紹了詳解Spring Security 中的四種權(quán)限控制方式,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10