Springboot @Import 详解
SpringBoot的@Import用于将指定的类实例注入之SpringIOCContainer中。
今天抽空在仔细看了下Springboot关于@Import的处理过程,记下来以后看。
1.@Import
先看Spring对它的注释(文档贴过来的),总结下来作用就是和xml配置的
但是如果要引入另一个xml文件形式配置的bean,则需要通过@ImportResource注解。
/** *Indicatesoneormore{@linkConfiguration@Configuration}classestoimport. * *Providesfunctionalityequivalenttothe{@code
}elementinSpringXML. *Allowsforimporting{@code@Configuration}classes,{@linkImportSelector}and *{@linkImportBeanDefinitionRegistrar}implementations,aswellasregularcomponent *classes(asof4.2;analogousto{@linkAnnotationConfigApplicationContext#register}). * * {@code@Bean}definitionsdeclaredinimported{@code@Configuration}classesshouldbe *accessedbyusing{@linkorg.springframework.beans.factory.annotation.Autowired@Autowired} *injection.Eitherthebeanitselfcanbeautowired,ortheconfigurationclassinstance *declaringthebeancanbeautowired.Thelatterapproachallowsforexplicit,IDE-friendly *navigationbetween{@code@Configuration}classmethods. * *
Maybedeclaredattheclasslevelorasameta-annotation. * *
IfXMLorothernon-{@code@Configuration}beandefinitionresourcesneedtobe *imported,usethe{@linkImportResource@ImportResource}annotationinstead. * *@authorChrisBeams *@authorJuergenHoeller *@since3.0 *@seeConfiguration *@seeImportSelector *@seeImportResource */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public@interfaceImport{ /** *{@linkConfiguration},{@linkImportSelector},{@linkImportBeanDefinitionRegistrar} *orregularcomponentclassestoimport. */ Class>[]value(); }
2.ImportSelector
因为@Import的实现有很多时候需要借助ImportSelector接口,所以我们再看下这个接口的描述,总结下来就是需要通过这个接口的实现去决定要引入哪些@Configuration。它如果实现了以下四个Aware接口,那么spring保证会在调用它之前先调用Aware接口的方法。
至于为什么要保证调用Aware,我个人觉得应该是你可以通过这些Aware去感知系统里边所有的环境变量,从而决定你具体的选择逻辑。
/** *Interfacetobeimplementedbytypesthatdeterminewhich@{@linkConfiguration} *class(es)shouldbeimportedbasedonagivenselectioncriteria,usuallyoneormore *annotationattributes. * *An{@linkImportSelector}mayimplementanyofthefollowing *{@linkorg.springframework.beans.factory.AwareAware}interfaces,andtheirrespective *methodswillbecalledpriorto{@link#selectImports}: *
-
*
- {@linkorg.springframework.context.EnvironmentAwareEnvironmentAware} *
- {@linkorg.springframework.beans.factory.BeanFactoryAwareBeanFactoryAware} *
- {@linkorg.springframework.beans.factory.BeanClassLoaderAwareBeanClassLoaderAware} *
- {@linkorg.springframework.context.ResourceLoaderAwareResourceLoaderAware} *
ImportSelectorsareusuallyprocessedinthesamewayasregular{@code@Import} *annotations,however,itisalsopossibletodeferselectionofimportsuntilall *{@code@Configuration}classeshavebeenprocessed(see{@linkDeferredImportSelector} *fordetails). * *@authorChrisBeams *@since3.1 *@seeDeferredImportSelector *@seeImport *@seeImportBeanDefinitionRegistrar *@seeConfiguration */ publicinterfaceImportSelector{ /** *Selectandreturnthenamesofwhichclass(es)shouldbeimportedbasedon *the{@linkAnnotationMetadata}oftheimporting@{@linkConfiguration}class. */ String[]selectImports(AnnotationMetadataimportingClassMetadata); }
3.Springboot对@Import注解的处理过程
Springboot对注解的处理都发生在AbstractApplicationContext->refresh()->invokeBeanFactoryPostProcessors(beanFactory)->ConfigurationClassPostProcessor->postProcessBeanDefinitionRegistry()方法中。
(稍微说下也免得我自己忘了,springboot初始化的普通context(非web)是AnnotationConfigApplicationContext,在初始化的时候会初始化两个工具类,AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner分别用来从annotationdriven的配置和xml的配置中读取beanDefinition并向context注册,那么在初始化AnnotatedBeanDefinitionReader的时候,会向BeanFactory注册一个ConfigurationClassPostProcessor用来处理所有的基于annotation的bean,这个ConfigurationClassPostProcessor是BeanFactoryPostProcessor的一个实现,springboot会保证在 invokeBeanFactoryPostProcessors(beanFactory)方法中调用注册到它上边的所有的BeanFactoryPostProcessor)
如下代码显示是通过ConfigurationClassParser类来转换的
//Parseeach@Configurationclass ConfigurationClassParserparser=newConfigurationClassParser( this.metadataReaderFactory,this.problemReporter,this.environment, this.resourceLoader,this.componentScanBeanNameGenerator,registry);
那么在ConfigurationClassParser->processConfigurationClass()->doProcessConfigurationClass()方法中我们找到了(这里边的流程还是很清楚的,分别按次序处理了@PropertySource,@ComponentScan,@Import,@ImportResource,在处理这些注解的时候是通过递归处理来保证所有的都被处理了)
//Processany@Importannotations processImports(configClass,sourceClass,getImports(sourceClass),true);
那接下来就看它到底是怎么做的.流程依然清晰:
首先,判断如果被import的是ImportSelector.class接口的实现,那么初始化这个被Import的类,然后调用它的selectImports方法去获得所需要的引入的configuration,然后递归处理
其次,判断如果被import的是ImportBeanDefinitionRegistrar接口的实现,那么初始化后将对当前对象的处理委托给这个ImportBeanDefinitionRegistrar(不是特别明白,只是我的猜测)
最后,将import引入的类作为一个正常的类来处理(调用最外层的doProcessConfigurationClass())
所以,从这里我们知道,如果你引入的是一个正常的component,那么会作为@Compoent或者@Configuration来处理,这样在BeanFactory里边可以通过getBean拿到,但如果你是ImportSelector或者ImportBeanDefinitionRegistrar接口的实现,那么spring并不会将他们注册到beanFactory中,而只是调用他们的方法。
privatevoidprocessImports(ConfigurationClassconfigClass,SourceClasscurrentSourceClass, CollectionimportCandidates,booleancheckForCircularImports){ if(importCandidates.isEmpty()){ return; } if(checkForCircularImports&&isChainedImportOnStack(configClass)){ this.problemReporter.error(newCircularImportProblem(configClass,this.importStack)); } else{ this.importStack.push(configClass); try{ for(SourceClasscandidate:importCandidates){ if(candidate.isAssignable(ImportSelector.class)){ //CandidateclassisanImportSelector->delegatetoittodetermineimports Class>candidateClass=candidate.loadClass(); ImportSelectorselector=BeanUtils.instantiateClass(candidateClass,ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector,this.environment,this.resourceLoader,this.registry); if(this.deferredImportSelectors!=null&&selectorinstanceofDeferredImportSelector){ this.deferredImportSelectors.add( newDeferredImportSelectorHolder(configClass,(DeferredImportSelector)selector)); } else{ String[]importClassNames=selector.selectImports(currentSourceClass.getMetadata()); Collection importSourceClasses=asSourceClasses(importClassNames); processImports(configClass,currentSourceClass,importSourceClasses,false); } } elseif(candidate.isAssignable(ImportBeanDefinitionRegistrar.class)){ //CandidateclassisanImportBeanDefinitionRegistrar-> //delegatetoittoregisteradditionalbeandefinitions Class>candidateClass=candidate.loadClass(); ImportBeanDefinitionRegistrarregistrar= BeanUtils.instantiateClass(candidateClass,ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar,this.environment,this.resourceLoader,this.registry); configClass.addImportBeanDefinitionRegistrar(registrar,currentSourceClass.getMetadata()); } else{ //CandidateclassnotanImportSelectororImportBeanDefinitionRegistrar-> //processitasan@Configurationclass this.importStack.registerImport( currentSourceClass.getMetadata(),candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch(BeanDefinitionStoreExceptionex){ throwex; } catch(Throwableex){ thrownewBeanDefinitionStoreException( "Failedtoprocessimportcandidatesforconfigurationclass["+ configClass.getMetadata().getClassName()+"]",ex); } finally{ this.importStack.pop(); } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。