欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https:github。comzq2599blogdemos在使用Import注解来注册bean的时候,Import注解的值可以是ImportSelector或者DeferredImportSelector的实现类,spring容器会实例化这个实现类,并执行其selectImports方法,那么问题来了:ImportSelector和DeferredImportSelector的区别在哪里,我们自定义Imort逻辑的时候该选择哪个呢?本文通过分析相关的spring源码来查找答案;全文概览本文由以下几部分组成:看官方文档;分析spring源码中对这两个接口的处理;实战验证;看官方文档先看官方文档看起,我选择了4。3。9版本在线文档(这是个Release版),地址:https:docs。spring。iospringdocs4。3。19。RELEASEjavadocapi原文:AvariationofImportSelectorthatrunsafterallConfigurationbeanshavebeenprocessed。ThistypeofselectorcanbeparticularlyusefulwhentheselectedimportsareConditional。ImplementationscanalsoextendtheOrderedinterfaceorusetheOrderannotationtoindicateaprecedenceagainstotherDeferredImportSelectors。我的理解:DeferredImportSelector是ImportSelector的一个扩展;ImportSelector实例的selectImports方法的执行时机,是在Configguration注解中的其他逻辑被处理之前,所谓的其他逻辑,包括对ImportResource、Bean这些注解的处理(注意,这里只是对Bean修饰的方法的处理,并不是立即调用Bean修饰的方法,这个区别很重要!);DeferredImportSelector实例的selectImports方法的执行时机,是在Configguration注解中的其他逻辑被处理完毕之后,所谓的其他逻辑,包括对ImportResource、Bean这些注解的处理;DeferredImportSelector的实现类可以用Order注解,或者实现Ordered接口来对selectImports的执行顺序排序;分析spring源码中对这两个接口的处理接下来看看源码:在springframework4。1。8。RELEASE工程中找到类ConfigurationClassParser。java,这里面有处理配置类的主要逻辑;找到方法parse(SetconfigCandidates):publicvoidparse(SetBeanDefinitionHolderconfigCandidates){this。deferredImportSelectorsnewLinkedListDeferredImportSelectorHolder();检查每个bean的定义for(BeanDefinitionHolderholder:configCandidates){BeanDefinitionbdholder。getBeanDefinition();try{if(bdinstanceofAnnotatedBeanDefinition){对于每个有注解的类,都执行方法parse(AnnotationMetadatametadata,StringbeanName)parse(((AnnotatedBeanDefinition)bd)。getMetadata(),holder。getBeanName());}elseif(bdinstanceofAbstractBeanDefinition((AbstractBeanDefinition)bd)。hasBeanClass()){parse(((AbstractBeanDefinition)bd)。getBeanClass(),holder。getBeanName());}else{parse(bd。getBeanClassName(),holder。getBeanName());}}catch(BeanDefinitionStoreExceptionex){}catch(Exceptionex){thrownewBeanDefinitionStoreException(Failedtoparseconfigurationclass〔bd。getBeanClassName()〕,ex);}}最后再处理DeferredImportSelector的实现类processDeferredImportSelectors();} 复制代码由以上代码可以大致看出DeferredImportSelector的实现类被最后放在processDeferredImportSelectors方法中处理,那么前面的parse(AnnotationMetadatametadata,StringbeanName)做了些什么呢?继续看;展开方法parse(AnnotationMetadatametadata,StringbeanName)里面,是执行processConfigurationClass方法;再展开processConfigurationClass方法,看到核心逻辑是调用doProcessConfigurationClass方法,展开看看:protectedfinalSourceClassdoProcessConfigurationClass(ConfigurationClassconfigClass,SourceClasssourceClass)throwsIOException{为了聚焦Import相关处理,此处略去部分不相关代码,不在这里展示了。。。。。。处理Import注解processImports(configClass,sourceClass,getImports(sourceClass),true);处理ImportResource注解if(sourceClass。getMetadata()。isAnnotated(ImportResource。class。getName())){AnnotationAttributesimportResourceAnnotationConfigUtils。attributesFor(sourceClass。getMetadata(),ImportResource。class);String〔〕resourcesimportResource。getStringArray(value);C?extendsBeanDefinitionReaderreaderClassimportResource。getClass(reader);for(Stringresource:resources){StringresolvedResourcethis。environment。resolveRequiredPlaceholders(resource);configClass。addImportedResource(resolvedResource,readerClass);}}处理Bean注解,注意是处理注解,不是执行Bean修饰的方法SetMethodMetadatabeanMethodssourceClass。getMetadata()。getAnnotatedMethods(Bean。class。getName());for(MethodMetadatamethodMetadata:beanMethods){configClass。addBeanMethod(newBeanMethod(methodMetadata,configClass));}处理Configuration类的父类,外面在调用doProcessConfigurationClass方法的时有迭代处理,确保所有父类的注解都会被处理if(sourceClass。getMetadata()。hasSuperClass()){StringsuperclasssourceClass。getMetadata()。getSuperClassName();if(!superclass。startsWith(java)!this。knownSuperclasses。containsKey(superclass)){this。knownSuperclasses。put(superclass,configClass);Superclassfound,returnitsannotationmetadataandrecursereturnsourceClass。getSuperClass();}}再也没有父类了,返回null表示当前Configuration处理完毕} 复制代码根据上述代码分析,可以梳理出下图中的逻辑: 现在需要再看看processImports和processDeferredImportSelectors这两个方法的具体代码;先看processImports方法:privatevoidprocessImports(ConfigurationClassconfigClass,SourceClasscurrentSourceClass,CollectionSourceClassimportCandidates,booleancheckForCircularImports)throwsIOException{if(importCandidates。isEmpty()){}if(checkForCircularImportsthis。importStack。contains(configClass)){this。problemReporter。error(newCircularImportProblem(configClass,this。importStack));}else{this。importStack。push(configClass);try{for(SourceClasscandidate:importCandidates){如果是ImportSelector接口的实现类,就在此处理if(candidate。isAssignable(ImportSelector。class)){CandidateclassisanImportSelectordelegatetoittodetermineimportsC?candidateClasscandidate。loadClass();实例化这些ImportSelector的实现类ImportSelectorselectorBeanUtils。instantiateClass(candidateClass,ImportSelector。class);如果这实现类还实现了BeanFactoryAware、EnvironmentAware这些接口,就要先执行这些接口中声明的方法invokeAwareMethods(selector);如果这个实现类也实现了DeferredImportSelector接口,就被加入到集合deferredImportSelectors中if(this。deferredImportSelectors!nullselectorinstanceofDeferredImportSelector){this。deferredImportSelectors。add(newDeferredImportSelectorHolder(configClass,(DeferredImportSelector)selector));}else{注意,这一行是关键代码!!!执行实现类的selectImports方法String〔〕importClassNamesselector。selectImports(currentSourceClass。getMetadata());CollectionSourceClassimportSourceClassesasSourceClasses(importClassNames);processImports(configClass,currentSourceClass,importSourceClasses,false);}}此处略去的和ImportSelector不相关的逻辑代码。。。。。。。。。}}catch(BeanDefinitionStoreExceptionex){}catch(Exceptionex){thrownewBeanDefinitionStoreException(Failedtoprocessimportcandidatesforconfigurationclass〔configClass。getMetadata()。getClassName()〕,ex);}finally{this。importStack。pop();}}} 复制代码以上代码有两个关键点:第一、当前被处理的类,如果实现了DeferredImportSelector接口,就被加入到集合deferredImportSelectors中;第二、当前被处理的类,如果没有实现DeferredImportSelector接口,但是实现了ImportSelector接口,就被执行selectImports方法;接下来看看processDeferredImportSelectors方法的源码,提前推测应该是处理集合deferredImportSelectors中的所有类,这些类都实现了DeferredImportSelector接口:privatevoidprocessDeferredImportSelectors(){ListDeferredImportSelectorHolderdeferredImportsthis。deferredImportSthis。deferredImportS按照Order注解或者Ordered接口进行排序Collections。sort(deferredImports,DEFERREDIMPORTCOMPARATOR);for(DeferredImportSelectorHolderdeferredImport:deferredImports){ConfigurationClassconfigClassdeferredImport。getConfigurationClass();try{此处是关键代码,执行DeferredImportSelector实现类的selectImports方法String〔〕importsdeferredImport。getImportSelector()。selectImports(configClass。getMetadata());processImports(configClass,asSourceClass(configClass),asSourceClasses(imports),false);}catch(BeanDefinitionStoreExceptionex){}catch(Exceptionex){thrownewBeanDefinitionStoreException(Failedtoprocessimportcandidatesforconfigurationclass〔configClass。getMetadata()。getClassName()〕,ex);}}} 复制代码至此,源码分析完毕了,从代码可以很清晰的看出ImportSelector与DeferredImportSelector的区别,就是selectImports方法执行时机有差别,这个差别期间,spring容器对此Configguration类做了些其他的逻辑:包括对ImportResource、Bean这些注解的处理(注意,这里只是对Bean修饰的方法的处理,并不是立即调用Bean修饰的方法,这个区别很重要!);实战验证接下来到了实战验证的环节了,本次实战的内容是创建一个springboot工程,在里面自定义三个ImportSelector接口的实现类,如果您不想敲代码,也可以去github下载源码,地址和链接信息如下表所示: 这个git项目中有多个文件夹,本章源码在文件夹customizeimportselector下,如下图红框所示: 开始编码吧:我们创建三个ImportSelector的实现类来检查其先后顺序,三个Selector类简介如下表,有两个是DeferredImportSelector的实现类,一个是ImportSelector的实现类,每个Selector负责向spring容器注册一种实例: 基于maven创建springboot框架的web工程,pom。xml内容如下:?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0http:maven。apache。orgxsdmaven4。0。0。xsdmodelVersion4。0。0modelVersiongroupIdcom。bolingcavalrygroupIdcustomizeimportselectorartifactIdversion0。0。1SNAPSHOTversionpackagingjarpackagingnamecustomizeimportselectornamedescriptionDemoprojectforSpringBootdescriptionparentgroupIdorg。springframework。bootgroupIdspringbootstarterparentartifactIdversion1。5。9。RELEASEversionrelativePath!lookupparentfromrepositoryparentpropertiesproject。build。sourceEncodingUTF8project。build。sourceEncodingproject。reporting。outputEncodingUTF8project。reporting。outputEncodingjava。version1。8java。versionpropertiesdependenciesdependencygroupIdorg。springframework。bootgroupIdspringbootstarterwebartifactIddependencydependencygroupIdorg。springframework。bootgroupIdspringbootstartertestartifactIdscopetestscopedependencydependenciesbuildpluginsplugingroupIdorg。springframework。bootgroupIdspringbootmavenpluginartifactIdpluginpluginsbuildproject 复制代码创建三个接口CustomizeService1、CustomizeService2、CustomizeService3,第一个源码如下,另外两个除了类名,其余部分一样:packagecom。bolingcavalry。customizeimportselector。publicinterfaceCustomizeService1{voidexecute();} 复制代码创建三个类,分别实现上面的三个接口,也是除了类名其余部分一样:packagecom。bolingcavalry。customizeimportselector。service。importcom。bolingcavalry。customizeimportselector。service。CustomizeService1;publicclassCustomizeServiceImpl1implementsCustomizeService1{publicCustomizeServiceImpl1(){System。out。println(construct:this。getClass()。getSimpleName());}Overridepublicvoidexecute(){System。out。println(execute:this。getClass()。getSimpleName());}} 复制代码创建CustomizeImportSelector1:packagecom。bolingcavalry。customizeimportselector。importorg。springframework。context。annotation。DeferredImportSimportorg。springframework。context。annotation。ImportSimportorg。springframework。core。annotation。Oimportorg。springframework。core。type。AnnotationMOrder(102)publicclassCustomizeImportSelector1implementsDeferredImportSelector{OverridepublicString〔〕selectImports(AnnotationMetadataannotationMetadata){System。out。println(selectImports:this。getClass()。getSimpleName());returnnewString〔〕{com。bolingcavalry。customizeimportselector。service。impl。CustomizeServiceImpl1};}} 复制代码创建CustomizeImportSelector2:packagecom。bolingcavalry。customizeimportselector。importorg。springframework。context。annotation。DeferredImportSimportorg。springframework。core。annotation。Oimportorg。springframework。core。type。AnnotationMOrder(101)publicclassCustomizeImportSelector2implementsDeferredImportSelector{OverridepublicString〔〕selectImports(AnnotationMetadataannotationMetadata){System。out。println(selectImports:this。getClass()。getSimpleName());returnnewString〔〕{com。bolingcavalry。customizeimportselector。service。impl。CustomizeServiceImpl2};}} 复制代码创建CustomizeImportSelector3,实现的是ImportSelector接口:packagecom。bolingcavalry。customizeimportselector。importorg。springframework。context。annotation。ImportSimportorg。springframework。core。annotation。Oimportorg。springframework。core。type。AnnotationMpublicclassCustomizeImportSelector3implementsImportSelector{OverridepublicString〔〕selectImports(AnnotationMetadataannotationMetadata){System。out。println(selectImports:this。getClass()。getSimpleName());returnnewString〔〕{com。bolingcavalry。customizeimportselector。service。impl。CustomizeServiceImpl3};}} 复制代码创建配置类,将CustomizeImportSelector1、CustomizeImportSelector2、CustomizeImportSelector3全部用Import注解引入:packagecom。bolingcavalry。importcom。bolingcavalry。customizeimportselector。selector。CustomizeImportSelector1;importcom。bolingcavalry。customizeimportselector。selector。CustomizeImportSelector2;importcom。bolingcavalry。customizeimportselector。selector。CustomizeImportSelector3;importorg。springframework。context。annotation。Cimportorg。springframework。context。annotation。IConfigurationImport({CustomizeImportSelector1。class,CustomizeImportSelector2。class,CustomizeImportSelector3。class})publicclassSysConfig{} 复制代码创建启动类CustomizeimportselectorApplication。java:packagecom。bolingcavalry。importorg。springframework。boot。SpringAimportorg。springframework。boot。autoconfigure。SpringBootASpringBootApplicationpublicclassCustomizeimportselectorApplication{publicstaticvoidmain(String〔〕args){SpringApplication。run(CustomizeimportselectorApplication。class,args);}} 复制代码启动应用,可见输入信息如下:2018090915:43:45。790INFO15364〔main〕c。b。c。CustomizeimportselectorApplication:StartingCustomizeimportselectorApplicationonDESKTOP82CCEBNwithPID15364(D:githubblogdemoscustomizeimportselectorargetclassesstartedby12167inD:githubblogdemoscustomizeimportselector)2018090915:43:45。791INFO15364〔main〕c。b。c。CustomizeimportselectorApplication:Noactiveprofileset,fallingbacktodefaultprofiles:default2018090915:43:45。825INFO15364〔main〕ationConfigEmbeddedWebApplicationContext:Refreshingorg。springframework。boot。context。embedded。AnnotationConfigEmbeddedWebApplicationContext641147d0:startupdate〔SunSep0915:43:45GMT08:002018〕;rootofcontexthierarchyselectImports:CustomizeImportSelector3selectImports:CustomizeImportSelector2selectImports:CustomizeImportSelector12018090915:43:46。425INFO15364〔main〕s。b。c。e。t。TomcatEmbeddedServletContainer:Tomcatinitializedwithport(s):8080(http)2018090915:43:46。430INFO15364〔main〕o。apache。catalina。core。StandardService:Startingservice〔Tomcat〕2018090915:43:46。431INFO15364〔main〕org。apache。catalina。core。StandardEngine:StartingServletEngine:ApacheTomcat8。5。232018090915:43:46。493INFO15364〔oststartStop1〕o。a。c。c。C。〔Tomcat〕。〔localhost〕。〔〕:InitializingSpringembeddedWebApplicationContext2018090915:43:46。493INFO15364〔oststartStop1〕o。s。web。context。ContextLoader:RootWebApplicationContext:initializationcompletedin670ms2018090915:43:46。569INFO15364〔oststartStop1〕o。s。b。w。servlet。ServletRegistrationBean:Mappingservlet:dispatcherServletto〔〕2018090915:43:46。572INFO15364〔oststartStop1〕o。s。b。w。servlet。FilterRegistrationBean:Mappingfilter:characterEncodingFilterto:〔〕2018090915:43:46。572INFO15364〔oststartStop1〕o。s。b。w。servlet。FilterRegistrationBean:Mappingfilter:hiddenHttpMethodFilterto:〔〕2018090915:43:46。572INFO15364〔oststartStop1〕o。s。b。w。servlet。FilterRegistrationBean:Mappingfilter:httpPutFormContentFilterto:〔〕2018090915:43:46。572INFO15364〔oststartStop1〕o。s。b。w。servlet。FilterRegistrationBean:Mappingfilter:requestContextFilterto:〔〕construct:CustomizeServiceImpl1construct:CustomizeServiceImpl2construct:CustomizeServiceImpl3 复制代码从上述信息可以看出:首先、三个selector实现类的selectImports方法执行顺序符合预期:先执行ImportSelector实现类的,再执行DeferredImportSelector实现类的,并且DeferredImportSelector实现类的执行顺序会按照Order的设置从小到大执行;其次、CustomizeServiceImpl1、CustomizeServiceImpl2、CustomizeServiceImpl3的实例化顺序并未受到影响;至此,ImportSelector与DeferredImportSelector的区别已经分析和验证完毕,随着对Configuration初始化处理逻辑的深入了解,我们可以定制出更灵活强大的配置逻辑,以符合业务需求;欢迎关注InfoQ:程序员欣宸 学习路上,你不孤单,欣宸原创一路相伴。。。