深入浅析SpringBoot中的自动装配
SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提。这次主要的议题是,来看看它是怎么样实现的,我们透过源代码来把握自动装配的来龙去脉。
一、自动装配过程分析
1.1、关于@SpringBootApplication
我们在编写SpringBoot项目时,@SpringBootApplication是最常见的注解了,我们可以看一下源代码:
/* *Copyright2012-2017theoriginalauthororauthors. * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. */ packageorg.springframework.boot.autoconfigure; importjava.lang.annotation.Documented; importjava.lang.annotation.ElementType; importjava.lang.annotation.Inherited; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; importorg.springframework.boot.SpringBootConfiguration; importorg.springframework.boot.context.TypeExcludeFilter; importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.ComponentScan; importorg.springframework.context.annotation.ComponentScan.Filter; importorg.springframework.context.annotation.Configuration; importorg.springframework.context.annotation.FilterType; importorg.springframework.core.annotation.AliasFor; /** *Indicatesa{@linkConfigurationconfiguration}classthatdeclaresoneormore *{@linkBean@Bean}methodsandalsotriggers{@linkEnableAutoConfiguration *auto-configuration}and{@linkComponentScancomponentscanning}.Thisisaconvenience *annotationthatisequivalenttodeclaring{@code@Configuration}, *{@code@EnableAutoConfiguration}and{@code@ComponentScan}. * *@authorPhillipWebb *@authorStephaneNicoll *@since1.2.0 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters={ @Filter(type=FilterType.CUSTOM,classes=TypeExcludeFilter.class), @Filter(type=FilterType.CUSTOM,classes=AutoConfigurationExcludeFilter.class)}) public@interfaceSpringBootApplication{ /** *Excludespecificauto-configurationclassessuchthattheywillneverbeapplied. *@returntheclassestoexclude */ @AliasFor(annotation=EnableAutoConfiguration.class,attribute="exclude") Class>[]exclude()default{}; /** *Excludespecificauto-configurationclassnamessuchthattheywillneverbe *applied. *@returntheclassnamestoexclude *@since1.3.0 */ @AliasFor(annotation=EnableAutoConfiguration.class,attribute="excludeName") String[]excludeName()default{}; /** *Basepackagestoscanforannotatedcomponents.Use{@link#scanBasePackageClasses} *foratype-safealternativetoString-basedpackagenames. *@returnbasepackagestoscan *@since1.3.0 */ @AliasFor(annotation=ComponentScan.class,attribute="basePackages") String[]scanBasePackages()default{}; /** *Type-safealternativeto{@link#scanBasePackages}forspecifyingthepackagesto *scanforannotatedcomponents.Thepackageofeachclassspecifiedwillbescanned. **Considercreatingaspecialno-opmarkerclassorinterfaceineachpackagethat *servesnopurposeotherthanbeingreferencedbythisattribute. *@returnbasepackagestoscan *@since1.3.0 */ @AliasFor(annotation=ComponentScan.class,attribute="basePackageClasses") Class>[]scanBasePackageClasses()default{}; }
这里面包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan,此处@ComponentScan由于没有指定扫描包,因此它默认扫描的是与该类同级的类或者同级包下的所有类,另外@SpringBootConfiguration,通过源码得知它是一个@Configuration:
/* *Copyright2012-2016theoriginalauthororauthors. * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. */ packageorg.springframework.boot; importjava.lang.annotation.Documented; importjava.lang.annotation.ElementType; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; importorg.springframework.context.annotation.Configuration; /** *IndicatesthataclassprovidesSpringBootapplication *{@linkConfiguration@Configuration}.CanbeusedasanalternativetotheSpring's *standard{@code@Configuration}annotationsothatconfigurationcanbefound *automatically(forexampleintests). **Applicationshouldonlyeverincludeone{@code@SpringBootConfiguration}and *mostidiomaticSpringBootapplicationswillinherititfrom *{@code@SpringBootApplication}. * *@authorPhillipWebb *@since1.4.0 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public@interfaceSpringBootConfiguration{ }
由此我们可以推断出@SpringBootApplication等同于@Configuration@ComponentScan@EnableAutoConfiguration
1.2、@EnableAutoConfiguration
一旦加上此注解,那么将会开启自动装配功能,简单点讲,Spring会试图在你的classpath下找到所有配置的Bean然后进行装配。当然装配Bean时,会根据若干个(Conditional)定制规则来进行初始化。我们看一下它的源码:
/* *Copyright2012-2017theoriginalauthororauthors. * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. */ packageorg.springframework.boot.autoconfigure; importjava.lang.annotation.Documented; importjava.lang.annotation.ElementType; importjava.lang.annotation.Inherited; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; importorg.springframework.boot.autoconfigure.condition.ConditionalOnBean; importorg.springframework.boot.autoconfigure.condition.ConditionalOnClass; importorg.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; importorg.springframework.boot.context.embedded.EmbeddedServletContainerFactory; importorg.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; importorg.springframework.context.annotation.Conditional; importorg.springframework.context.annotation.Configuration; importorg.springframework.context.annotation.Import; importorg.springframework.core.io.support.SpringFactoriesLoader; /** *Enableauto-configurationoftheSpringApplicationContext,attemptingtoguessand *configurebeansthatyouarelikelytoneed.Auto-configurationclassesareusually *appliedbasedonyourclasspathandwhatbeansyouhavedefined.Forexample,Ifyou *have{@codetomcat-embedded.jar}onyourclasspathyouarelikelytowanta *{@linkTomcatEmbeddedServletContainerFactory}(unlessyouhavedefinedyourown *{@linkEmbeddedServletContainerFactory}bean). **Whenusing{@linkSpringBootApplication},theauto-configurationofthecontextis *automaticallyenabledandaddingthisannotationhasthereforenoadditionaleffect. *
*Auto-configurationtriestobeasintelligentaspossibleandwillback-awayasyou *definemoreofyourownconfiguration.Youcanalwaysmanually{@link#exclude()}any *configurationthatyouneverwanttoapply(use{@link#excludeName()}ifyoudon't *haveaccesstothem).Youcanalsoexcludethemviathe *{@codespring.autoconfigure.exclude}property.Auto-configurationisalwaysapplied *afteruser-definedbeanshavebeenregistered. *
*Thepackageoftheclassthatisannotatedwith{@code@EnableAutoConfiguration}, *usuallyvia{@code@SpringBootApplication},hasspecificsignificanceandisoftenused *asa'default'.Forexample,itwillbeusedwhenscanningfor{@code@Entity}classes. *Itisgenerallyrecommendedthatyouplace{@code@EnableAutoConfiguration}(ifyou're *notusing{@code@SpringBootApplication})inarootpackagesothatallsub-packages *andclassescanbesearched. *
*Auto-configurationclassesareregularSpring{@linkConfiguration}beans.Theyare *locatedusingthe{@linkSpringFactoriesLoader}mechanism(keyedagainstthisclass). *Generallyauto-configurationbeansare{@linkConditional@Conditional}beans(most *oftenusing{@linkConditionalOnClass@ConditionalOnClass}and *{@linkConditionalOnMissingBean@ConditionalOnMissingBean}annotations). * *@authorPhillipWebb *@authorStephaneNicoll *@seeConditionalOnBean *@seeConditionalOnMissingBean *@seeConditionalOnClass *@seeAutoConfigureAfter *@seeSpringBootApplication */ @SuppressWarnings("deprecation") @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public@interfaceEnableAutoConfiguration{ StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration"; /** *Excludespecificauto-configurationclassessuchthattheywillneverbeapplied. *@returntheclassestoexclude */ Class>[]exclude()default{}; /** *Excludespecificauto-configurationclassnamessuchthattheywillneverbe *applied. *@returntheclassnamestoexclude *@since1.3.0 */ String[]excludeName()default{}; }
虽然根据文档注释的说明它指点我们去看EnableAutoConfigurationImportSelector。但是该类在SpringBoot1.5.X版本已经过时了,因此我们看一下它的父类AutoConfigurationImportSelector:
/* *Copyright2012-2017theoriginalauthororauthors. * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. */ packageorg.springframework.boot.autoconfigure; importjava.io.IOException; importjava.util.ArrayList; importjava.util.Arrays; importjava.util.Collections; importjava.util.HashSet; importjava.util.LinkedHashSet; importjava.util.List; importjava.util.Map; importjava.util.Set; importjava.util.concurrent.TimeUnit; importorg.apache.commons.logging.Log; importorg.apache.commons.logging.LogFactory; importorg.springframework.beans.BeansException; importorg.springframework.beans.factory.Aware; importorg.springframework.beans.factory.BeanClassLoaderAware; importorg.springframework.beans.factory.BeanFactory; importorg.springframework.beans.factory.BeanFactoryAware; importorg.springframework.beans.factory.NoSuchBeanDefinitionException; importorg.springframework.beans.factory.config.ConfigurableListableBeanFactory; importorg.springframework.boot.bind.RelaxedPropertyResolver; importorg.springframework.context.EnvironmentAware; importorg.springframework.context.ResourceLoaderAware; importorg.springframework.context.annotation.DeferredImportSelector; importorg.springframework.core.Ordered; importorg.springframework.core.annotation.AnnotationAttributes; importorg.springframework.core.env.ConfigurableEnvironment; importorg.springframework.core.env.Environment; importorg.springframework.core.io.ResourceLoader; importorg.springframework.core.io.support.SpringFactoriesLoader; importorg.springframework.core.type.AnnotationMetadata; importorg.springframework.core.type.classreading.CachingMetadataReaderFactory; importorg.springframework.core.type.classreading.MetadataReaderFactory; importorg.springframework.util.Assert; importorg.springframework.util.ClassUtils; importorg.springframework.util.StringUtils; /** *{@linkDeferredImportSelector}tohandle{@linkEnableAutoConfiguration *auto-configuration}.Thisclasscanalsobesubclassedifacustomvariantof *{@linkEnableAutoConfiguration@EnableAutoConfiguration}.isneeded. * *@authorPhillipWebb *@authorAndyWilkinson *@authorStephaneNicoll *@authorMadhuraBhave *@since1.3.0 *@seeEnableAutoConfiguration */ publicclassAutoConfigurationImportSelector implementsDeferredImportSelector,BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware,EnvironmentAware,Ordered{ privatestaticfinalString[]NO_IMPORTS={}; privatestaticfinalLoglogger=LogFactory .getLog(AutoConfigurationImportSelector.class); privateConfigurableListableBeanFactorybeanFactory; privateEnvironmentenvironment; privateClassLoaderbeanClassLoader; privateResourceLoaderresourceLoader; @Override publicString[]selectImports(AnnotationMetadataannotationMetadata){ if(!isEnabled(annotationMetadata)){ returnNO_IMPORTS; } try{ AutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributesattributes=getAttributes(annotationMetadata); Listconfigurations=getCandidateConfigurations(annotationMetadata, attributes); configurations=removeDuplicates(configurations); configurations=sort(configurations,autoConfigurationMetadata); Set exclusions=getExclusions(annotationMetadata,attributes); checkExcludedClasses(configurations,exclusions); configurations.removeAll(exclusions); configurations=filter(configurations,autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations,exclusions); returnconfigurations.toArray(newString[configurations.size()]); } catch(IOExceptionex){ thrownewIllegalStateException(ex); } } protectedbooleanisEnabled(AnnotationMetadatametadata){ returntrue; } /** *Returntheappropriate{@linkAnnotationAttributes}fromthe *{@linkAnnotationMetadata}.Bydefaultthismethodwillreturnattributesfor *{@link#getAnnotationClass()}. *@parammetadatatheannotationmetadata *@returnannotationattributes */ protectedAnnotationAttributesgetAttributes(AnnotationMetadatametadata){ Stringname=getAnnotationClass().getName(); AnnotationAttributesattributes=AnnotationAttributes .fromMap(metadata.getAnnotationAttributes(name,true)); Assert.notNull(attributes, "Noauto-configurationattributesfound.Is"+metadata.getClassName() +"annotatedwith"+ClassUtils.getShortName(name)+"?"); returnattributes; } /** *Returnthesourceannotationclassusedbytheselector. *@returntheannotationclass */ protectedClass>getAnnotationClass(){ returnEnableAutoConfiguration.class; } /** *Returntheauto-configurationclassnamesthatshouldbeconsidered.Bydefault *thismethodwillloadcandidatesusing{@linkSpringFactoriesLoader}with *{@link#getSpringFactoriesLoaderFactoryClass()}. *@parammetadatathesourcemetadata *@paramattributesthe{@link#getAttributes(AnnotationMetadata)annotation *attributes} *@returnalistofcandidateconfigurations */ protectedList getCandidateConfigurations(AnnotationMetadatametadata, AnnotationAttributesattributes){ List configurations=SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()); Assert.notEmpty(configurations, "NoautoconfigurationclassesfoundinMETA-INF/spring.factories.Ifyou" +"areusingacustompackaging,makesurethatfileiscorrect."); returnconfigurations; } /** *Returntheclassusedby{@linkSpringFactoriesLoader}toloadconfiguration *candidates. *@returnthefactoryclass */ protectedClass>getSpringFactoriesLoaderFactoryClass(){ returnEnableAutoConfiguration.class; } privatevoidcheckExcludedClasses(List configurations, Set exclusions){ List invalidExcludes=newArrayList (exclusions.size()); for(Stringexclusion:exclusions){ if(ClassUtils.isPresent(exclusion,getClass().getClassLoader()) &&!configurations.contains(exclusion)){ invalidExcludes.add(exclusion); } } if(!invalidExcludes.isEmpty()){ handleInvalidExcludes(invalidExcludes); } } /** *Handleanyinvalidexcludesthathavebeenspecified. *@paraminvalidExcludesthelistofinvalidexcludes(willalwayshaveatleastone *element) */ protectedvoidhandleInvalidExcludes(List invalidExcludes){ StringBuildermessage=newStringBuilder(); for(Stringexclude:invalidExcludes){ message.append("\t-").append(exclude).append(String.format("%n")); } thrownewIllegalStateException(String .format("Thefollowingclassescouldnotbeexcludedbecausetheyare" +"notauto-configurationclasses:%n%s",message)); } /** *Returnanyexclusionsthatlimitthecandidateconfigurations. *@parammetadatathesourcemetadata *@paramattributesthe{@link#getAttributes(AnnotationMetadata)annotation *attributes} *@returnexclusionsoranemptyset */ protectedSet getExclusions(AnnotationMetadatametadata, AnnotationAttributesattributes){ Set excluded=newLinkedHashSet (); excluded.addAll(asList(attributes,"exclude")); excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); excluded.addAll(getExcludeAutoConfigurationsProperty()); returnexcluded; } privateList getExcludeAutoConfigurationsProperty(){ if(getEnvironment()instanceofConfigurableEnvironment){ RelaxedPropertyResolverresolver=newRelaxedPropertyResolver( this.environment,"spring.autoconfigure."); Map properties=resolver.getSubProperties("exclude"); if(properties.isEmpty()){ returnCollections.emptyList(); } List excludes=newArrayList (); for(Map.Entry entry:properties.entrySet()){ Stringname=entry.getKey(); Objectvalue=entry.getValue(); if(name.isEmpty()||name.startsWith("[")&&value!=null){ excludes.addAll(newHashSet (Arrays.asList(StringUtils .tokenizeToStringArray(String.valueOf(value),",")))); } } returnexcludes; } RelaxedPropertyResolverresolver=newRelaxedPropertyResolver(getEnvironment(), "spring.autoconfigure."); String[]exclude=resolver.getProperty("exclude",String[].class); return(Arrays.asList(exclude==null?newString[0]:exclude)); } privateList sort(List configurations, AutoConfigurationMetadataautoConfigurationMetadata)throwsIOException{ configurations=newAutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata).getInPriorityOrder(configurations); returnconfigurations; } privateList filter(List configurations, AutoConfigurationMetadataautoConfigurationMetadata){ longstartTime=System.nanoTime(); String[]candidates=configurations.toArray(newString[configurations.size()]); boolean[]skip=newboolean[candidates.length]; booleanskipped=false; for(AutoConfigurationImportFilterfilter:getAutoConfigurationImportFilters()){ invokeAwareMethods(filter); boolean[]match=filter.match(candidates,autoConfigurationMetadata); for(inti=0;i result=newArrayList (candidates.length); for(inti=0;i (result); } protectedList getAutoConfigurationImportFilters(){ returnSpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); } privateMetadataReaderFactorygetMetadataReaderFactory(){ try{ returngetBeanFactory().getBean( SharedMetadataReaderFactoryContextInitializer.BEAN_NAME, MetadataReaderFactory.class); } catch(NoSuchBeanDefinitionExceptionex){ returnnewCachingMetadataReaderFactory(this.resourceLoader); } } protectedfinal List removeDuplicates(List list){ returnnewArrayList (newLinkedHashSet (list)); } protectedfinalList asList(AnnotationAttributesattributes,Stringname){ String[]value=attributes.getStringArray(name); returnArrays.asList(value==null?newString[0]:value); } privatevoidfireAutoConfigurationImportEvents(List configurations, Set exclusions){ List listeners=getAutoConfigurationImportListeners(); if(!listeners.isEmpty()){ AutoConfigurationImportEventevent=newAutoConfigurationImportEvent(this, configurations,exclusions); for(AutoConfigurationImportListenerlistener:listeners){ invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event); } } } protectedList getAutoConfigurationImportListeners(){ returnSpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader); } privatevoidinvokeAwareMethods(Objectinstance){ if(instanceinstanceofAware){ if(instanceinstanceofBeanClassLoaderAware){ ((BeanClassLoaderAware)instance) .setBeanClassLoader(this.beanClassLoader); } if(instanceinstanceofBeanFactoryAware){ ((BeanFactoryAware)instance).setBeanFactory(this.beanFactory); } if(instanceinstanceofEnvironmentAware){ ((EnvironmentAware)instance).setEnvironment(this.environment); } if(instanceinstanceofResourceLoaderAware){ ((ResourceLoaderAware)instance).setResourceLoader(this.resourceLoader); } } } @Override publicvoidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException{ Assert.isInstanceOf(ConfigurableListableBeanFactory.class,beanFactory); this.beanFactory=(ConfigurableListableBeanFactory)beanFactory; } protectedfinalConfigurableListableBeanFactorygetBeanFactory(){ returnthis.beanFactory; } @Override publicvoidsetBeanClassLoader(ClassLoaderclassLoader){ this.beanClassLoader=classLoader; } protectedClassLoadergetBeanClassLoader(){ returnthis.beanClassLoader; } @Override publicvoidsetEnvironment(Environmentenvironment){ this.environment=environment; } protectedfinalEnvironmentgetEnvironment(){ returnthis.environment; } @Override publicvoidsetResourceLoader(ResourceLoaderresourceLoader){ this.resourceLoader=resourceLoader; } protectedfinalResourceLoadergetResourceLoader(){ returnthis.resourceLoader; } @Override publicintgetOrder(){ returnOrdered.LOWEST_PRECEDENCE-1; } }
首先该类实现了DeferredImportSelector接口,这个接口继承了ImportSelector:
/* *Copyright2002-2013theoriginalauthororauthors. * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. */ packageorg.springframework.context.annotation; importorg.springframework.core.type.AnnotationMetadata; /** *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); }
该接口主要是为了导入@Configuration的配置项,而DeferredImportSelector是延期导入,当所有的@Configuration都处理过后才会执行。
回过头来我们看一下AutoConfigurationImportSelector的selectImport方法:
@Override publicString[]selectImports(AnnotationMetadataannotationMetadata){ if(!isEnabled(annotationMetadata)){ returnNO_IMPORTS; } try{ AutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributesattributes=getAttributes(annotationMetadata); Listconfigurations=getCandidateConfigurations(annotationMetadata, attributes); configurations=removeDuplicates(configurations); configurations=sort(configurations,autoConfigurationMetadata); Set exclusions=getExclusions(annotationMetadata,attributes); checkExcludedClasses(configurations,exclusions); configurations.removeAll(exclusions); configurations=filter(configurations,autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations,exclusions); returnconfigurations.toArray(newString[configurations.size()]); } catch(IOExceptionex){ thrownewIllegalStateException(ex); } }
该方法刚开始会先判断是否进行自动装配,而后会从META-INF/spring-autoconfigure-metadata.properties读取元数据与元数据的相关属性,紧接着会调用getCandidateConfigurations方法:
/** *Returntheauto-configurationclassnamesthatshouldbeconsidered.Bydefault *thismethodwillloadcandidatesusing{@linkSpringFactoriesLoader}with *{@link#getSpringFactoriesLoaderFactoryClass()}. *@parammetadatathesourcemetadata *@paramattributesthe{@link#getAttributes(AnnotationMetadata)annotation *attributes} *@returnalistofcandidateconfigurations */ protectedListgetCandidateConfigurations(AnnotationMetadatametadata, AnnotationAttributesattributes){ List configurations=SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()); Assert.notEmpty(configurations, "NoautoconfigurationclassesfoundinMETA-INF/spring.factories.Ifyou" +"areusingacustompackaging,makesurethatfileiscorrect."); returnconfigurations; } /** *Returntheclassusedby{@linkSpringFactoriesLoader}toloadconfiguration *candidates. *@returnthefactoryclass */ protectedClass>getSpringFactoriesLoaderFactoryClass(){ returnEnableAutoConfiguration.class; }
在这里又遇到我们的老熟人了--SpringFactoryiesLoader,它会读取META-INF/spring.factories下的EnableAutoConfiguration的配置,紧接着在进行排除与过滤,进而得到需要装配的类。最后让所有配置在META-INF/spring.factories下的AutoConfigurationImportListener执行AutoConfigurationImportEvent事件,代码如下:
privatevoidfireAutoConfigurationImportEvents(Listconfigurations, Set exclusions){ List listeners=getAutoConfigurationImportListeners(); if(!listeners.isEmpty()){ AutoConfigurationImportEventevent=newAutoConfigurationImportEvent(this, configurations,exclusions); for(AutoConfigurationImportListenerlistener:listeners){ invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event); } } } protectedList getAutoConfigurationImportListeners(){ returnSpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader); }
二、何时进行自动装配
在前面的环节里只是最终要确定哪些类需要被装配,在SpringBoot时何时处理这些自动装配的类呢?下面我们简要的分析一下:
2.1、AbstractApplicationContext的refresh方法:
这个方法老生常谈了其中请大家关注一下这个方法:
//Invokefactoryprocessorsregisteredasbeansinthecontext. invokeBeanFactoryPostProcessors(beanFactory);
在这里是处理BeanFactoryPostProcessor的,那么我们在来看一下这个接口BeanDefinitionRegistryPostProcessor:
/* *Copyright2002-2010theoriginalauthororauthors. * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. */ packageorg.springframework.beans.factory.support; importorg.springframework.beans.BeansException; importorg.springframework.beans.factory.config.BeanFactoryPostProcessor; /** *Extensiontothestandard{@linkBeanFactoryPostProcessor}SPI,allowingfor *theregistrationoffurtherbeandefinitionsbeforeregular *BeanFactoryPostProcessordetectionkicksin.Inparticular, *BeanDefinitionRegistryPostProcessormayregisterfurtherbeandefinitions *whichinturndefineBeanFactoryPostProcessorinstances. * *@authorJuergenHoeller *@since3.0.1 *@seeorg.springframework.context.annotation.ConfigurationClassPostProcessor */ publicinterfaceBeanDefinitionRegistryPostProcessorextendsBeanFactoryPostProcessor{ /** *Modifytheapplicationcontext'sinternalbeandefinitionregistryafterits *standardinitialization.Allregularbeandefinitionswillhavebeenloaded, *butnobeanswillhavebeeninstantiatedyet.Thisallowsforaddingfurther *beandefinitionsbeforethenextpost-processingphasekicksin. *@paramregistrythebeandefinitionregistryusedbytheapplicationcontext *@throwsorg.springframework.beans.BeansExceptionincaseoferrors */ voidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistryregistry)throwsBeansException; }
该接口继承了BeanFactoryPostProcessor。
2.2、ConfigurationClassPostProcessor类
该类主要处理@Configuration注解的,它实现了BeanDefinitionRegistryPostProcessor, 那么也间接实现了BeanFactoryPostProcessor,关键代码如下:
@Override publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory){ intfactoryId=System.identityHashCode(beanFactory); if(this.factoriesPostProcessed.contains(factoryId)){ thrownewIllegalStateException( "postProcessBeanFactoryalreadycalledonthispost-processoragainst"+beanFactory); } this.factoriesPostProcessed.add(factoryId); if(!this.registriesPostProcessed.contains(factoryId)){ //BeanDefinitionRegistryPostProcessorhookapparentlynotsupported... //SimplycallprocessConfigurationClasseslazilyatthispointthen. processConfigBeanDefinitions((BeanDefinitionRegistry)beanFactory); } enhanceConfigurationClasses(beanFactory); beanFactory.addBeanPostProcessor(newImportAwareBeanPostProcessor(beanFactory)); } /** *Buildandvalidateaconfigurationmodelbasedontheregistryof *{@linkConfiguration}classes. */ publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistryregistry){ //.....省略部分代码 //Parseeach@Configurationclass ConfigurationClassParserparser=newConfigurationClassParser( this.metadataReaderFactory,this.problemReporter,this.environment, this.resourceLoader,this.componentScanBeanNameGenerator,registry); Setcandidates=newLinkedHashSet (configCandidates); Set alreadyParsed=newHashSet (configCandidates.size()); do{ parser.parse(candidates); parser.validate(); Set configClasses=newLinkedHashSet (parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); //Readthemodelandcreatebeandefinitionsbasedonitscontent if(this.reader==null){ this.reader=newConfigurationClassBeanDefinitionReader( registry,this.sourceExtractor,this.resourceLoader,this.environment, this.importBeanNameGenerator,parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if(registry.getBeanDefinitionCount()>candidateNames.length){ String[]newCandidateNames=registry.getBeanDefinitionNames(); Set oldCandidateNames=newHashSet (Arrays.asList(candidateNames)); Set alreadyParsedClasses=newHashSet (); for(ConfigurationClassconfigurationClass:alreadyParsed){ alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for(StringcandidateName:newCandidateNames){ if(!oldCandidateNames.contains(candidateName)){ BeanDefinitionbd=registry.getBeanDefinition(candidateName); if(ConfigurationClassUtils.checkConfigurationClassCandidate(bd,this.metadataReaderFactory)&& !alreadyParsedClasses.contains(bd.getBeanClassName())){ candidates.add(newBeanDefinitionHolder(bd,candidateName)); } } } candidateNames=newCandidateNames; } } while(!candidates.isEmpty()); //....省略部分代码 }
其实这里注释已经很清楚了,我们可以清楚的看到解析每一个@ConfigurationClass的关键类是:ConfigurationClassParser,那么我们继续看一看这个类的parse方法:
publicvoidparse(SetconfigCandidates){ this.deferredImportSelectors=newLinkedList (); for(BeanDefinitionHolderholder:configCandidates){ BeanDefinitionbd=holder.getBeanDefinition(); try{ if(bdinstanceofAnnotatedBeanDefinition){ 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){ throwex; } catch(Throwableex){ thrownewBeanDefinitionStoreException( "Failedtoparseconfigurationclass["+bd.getBeanClassName()+"]",ex); } } processDeferredImportSelectors(); }
在这里大家留意一下最后一句processDeferredImportSelectors方法,在这里将会对DeferredImportSelector进行处理,这样我们就和AutoConfigurationSelectImporter结合到一起了:
privatevoidprocessDeferredImportSelectors(){ ListdeferredImports=this.deferredImportSelectors; this.deferredImportSelectors=null; Collections.sort(deferredImports,DEFERRED_IMPORT_COMPARATOR); for(DeferredImportSelectorHolderdeferredImport:deferredImports){ ConfigurationClassconfigClass=deferredImport.getConfigurationClass(); try{ String[]imports=deferredImport.getImportSelector().selectImports(configClass.getMetadata()); processImports(configClass,asSourceClass(configClass),asSourceClasses(imports),false); } catch(BeanDefinitionStoreExceptionex){ throwex; } catch(Throwableex){ thrownewBeanDefinitionStoreException( "Failedtoprocessimportcandidatesforconfigurationclass["+ configClass.getMetadata().getClassName()+"]",ex); } } }
请大家关注这句代码:String[]imports=deferredImport.getImportSelector().selectImports(configClass.getMetadata());在这里deferredImport的类型为DeferredImportSelectorHolder:
privatestaticclassDeferredImportSelectorHolder{ privatefinalConfigurationClassconfigurationClass; privatefinalDeferredImportSelectorimportSelector; publicDeferredImportSelectorHolder(ConfigurationClassconfigClass,DeferredImportSelectorselector){ this.configurationClass=configClass; this.importSelector=selector; } publicConfigurationClassgetConfigurationClass(){ returnthis.configurationClass; } publicDeferredImportSelectorgetImportSelector(){ returnthis.importSelector; } }
在这个内部类里持有了一个DeferredImportSelector的引用,至此将会执行自动装配的所有操作
三、总结
1)自动装配还是利用了SpringFactoriesLoader来加载META-INF/spring.factoires文件里所有配置的EnableAutoConfgruation,它会经过exclude和filter等操作,最终确定要装配的类
2) 处理@Configuration的核心还是ConfigurationClassPostProcessor,这个类实现了BeanFactoryPostProcessor,因此当AbstractApplicationContext执行refresh方法里的invokeBeanFactoryPostProcessors(beanFactory)方法时会执行自动装配
以上所述是小编给大家介绍的SpringBoot中的自动装配,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!