您好,登錄后才能下訂單哦!
剛做后端開發(fā)的時(shí)候,最早接觸的是基礎(chǔ)的spring,為了引用二方包提供bean,還需要在xml中增加對(duì)應(yīng)的包<context:component-scan base-package="xxx" />
?或者增加注解@ComponentScan({ "xxx"})
。當(dāng)時(shí)覺得挺urgly的,但也沒有去研究有沒有更好的方式。
直到接觸Spring Boot 后,發(fā)現(xiàn)其可以自動(dòng)引入二方包的bean。不過一直沒有看這塊的實(shí)現(xiàn)原理。直到最近面試的時(shí)候被問到。所以就看了下實(shí)現(xiàn)邏輯。
講原理前先說下使用姿勢。
在project A中定義一個(gè)bean。
package?com.wangzhi; import?org.springframework.stereotype.Service; @Service public?class?Dog?{ }
并在該project的resources/META-INF/
下創(chuàng)建一個(gè)叫spring.factories
的文件,該文件內(nèi)容如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.wangzhi.Dog
然后在project B中引用project A的jar包。
projectA代碼如下:
package?com.wangzhi.springbootdemo; import?org.springframework.boot.SpringApplication; import?org.springframework.boot.autoconfigure.EnableAutoConfiguration; import?org.springframework.boot.autoconfigure.SpringBootApplication; import?org.springframework.context.ConfigurableApplicationContext; import?org.springframework.context.annotation.ComponentScan; @EnableAutoConfiguration public?class?SpringBootDemoApplication?{ ????public?static?void?main(String[]?args)?{ ????????ConfigurableApplicationContext?context?=?SpringApplication.run(SpringBootDemoApplication.class,?args); ????????System.out.println(context.getBean(com.wangzhi.Dog.class)); ????} }
打印結(jié)果:
com.wangzhi.Dog@3148f668
總體分為兩個(gè)部分:一是收集所有spring.factories
中EnableAutoConfiguration
相關(guān)bean的類,二是將得到的類注冊(cè)到spring容器中。
在spring容器啟動(dòng)時(shí),會(huì)調(diào)用到AutoConfigurationImportSelector#getAutoConfigurationEntry
protected?AutoConfigurationEntry?getAutoConfigurationEntry( ????????AutoConfigurationMetadata?autoConfigurationMetadata, ????????AnnotationMetadata?annotationMetadata)?{ ????if?(!isEnabled(annotationMetadata))?{ ????????return?EMPTY_ENTRY; ????} ????//?EnableAutoConfiguration注解的屬性:exclude,excludeName等 ????AnnotationAttributes?attributes?=?getAttributes(annotationMetadata); ????//?得到所有的Configurations ????List<String>?configurations?=?getCandidateConfigurations(annotationMetadata, ????????????attributes); ????//?去重 ????configurations?=?removeDuplicates(configurations); ????//?刪除掉exclude中指定的類 ????Set<String>?exclusions?=?getExclusions(annotationMetadata,?attributes); ????checkExcludedClasses(configurations,?exclusions); ????configurations.removeAll(exclusions); ????configurations?=?filter(configurations,?autoConfigurationMetadata); ????fireAutoConfigurationImportEvents(configurations,?exclusions); ????return?new?AutoConfigurationEntry(configurations,?exclusions); }
getCandidateConfigurations
會(huì)調(diào)用到方法loadFactoryNames
:
public?static?List<String>?loadFactoryNames(Class<?>?factoryClass,?@Nullable?ClassLoader?classLoader)?{ ????????//?factoryClassName為org.springframework.boot.autoconfigure.EnableAutoConfiguration String?factoryClassName?=?factoryClass.getName(); ????????//?該方法返回的是所有spring.factories文件中key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的類路徑 return?loadSpringFactories(classLoader).getOrDefault(factoryClassName,?Collections.emptyList()); } public?static?final?String?FACTORIES_RESOURCE_LOCATION?=?"META-INF/spring.factories"; private?static?Map<String,?List<String>>?loadSpringFactories(@Nullable?ClassLoader?classLoader)?{ MultiValueMap<String,?String>?result?=?cache.get(classLoader); if?(result?!=?null)?{ return?result; } try?{ ????????????//?找到所有的"META-INF/spring.factories" Enumeration<URL>?urls?=?(classLoader?!=?null?? classLoader.getResources(FACTORIES_RESOURCE_LOCATION)?: ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result?=?new?LinkedMultiValueMap<>(); while?(urls.hasMoreElements())?{ URL?url?=?urls.nextElement(); UrlResource?resource?=?new?UrlResource(url); ????????????????//?讀取文件內(nèi)容,properties類似于HashMap,包含了屬性的key和value Properties?properties?=?PropertiesLoaderUtils.loadProperties(resource); for?(Map.Entry<?,??>?entry?:?properties.entrySet())?{ String?factoryClassName?=?((String)?entry.getKey()).trim(); ????????????????????//?屬性文件中可以用','分割多個(gè)value for?(String?factoryName?:?StringUtils.commaDelimitedListToStringArray((String)?entry.getValue()))?{ result.add(factoryClassName,?factoryName.trim()); } } } cache.put(classLoader,?result); return?result; } catch?(IOException?ex)?{ throw?new?IllegalArgumentException("Unable?to?load?factories?from?location?["?+ FACTORIES_RESOURCE_LOCATION?+?"]",?ex); } }
在上面的流程中得到了所有在spring.factories
中指定的bean的類路徑,在processGroupImports
方法中會(huì)以處理@import注解一樣的邏輯將其導(dǎo)入進(jìn)容器。
public?void?processGroupImports()?{ ????for?(DeferredImportSelectorGrouping?grouping?:?this.groupings.values())?{ ????????//?getImports即上面得到的所有類路徑的封裝 ????????grouping.getImports().forEach(entry?->?{ ????????????ConfigurationClass?configurationClass?=?this.configurationClasses.get( ????????????????????entry.getMetadata()); ????????????try?{ ????????????????//?和處理@Import注解一樣 ????????????????processImports(configurationClass,?asSourceClass(configurationClass), ????????????????????????asSourceClasses(entry.getImportClassName()),?false); ????????????} ????????????catch?(BeanDefinitionStoreException?ex)?{ ????????????????throw?ex; ????????????} ????????????catch?(Throwable?ex)?{ ????????????????throw?new?BeanDefinitionStoreException( ????????????????????????"Failed?to?process?import?candidates?for?configuration?class?["?+ ????????????????????????????????configurationClass.getMetadata().getClassName()?+?"]",?ex); ????????????} ????????}); ????} } private?void?processImports(ConfigurationClass?configClass,?SourceClass?currentSourceClass, Collection<SourceClass>?importCandidates,?boolean?checkForCircularImports)?{ ... ????//?遍歷收集到的類路徑 ????for?(SourceClass?candidate?:?importCandidates)?{ ???????... ????????//如果candidate是ImportSelector或ImportBeanDefinitionRegistrar類型其處理邏輯會(huì)不一樣,這里不關(guān)注 ????? //?Candidate?class?not?an?ImportSelector?or?ImportBeanDefinitionRegistrar?-> //?process?it?as?an?@Configuration?class this.importStack.registerImport( currentSourceClass.getMetadata(),?candidate.getMetadata().getClassName()); //?當(dāng)作?@Configuration?處理 ????????processConfigurationClass(candidate.asConfigClass(configClass)); ???... } ???????????? ????... }
可以看到,在第一步收集的bean類定義,最終會(huì)被以Configuration
一樣的處理方式注冊(cè)到容器中。
@EnableAutoConfiguration
注解簡化了導(dǎo)入了二方包bean的成本。提供一個(gè)二方包給其他應(yīng)用使用,只需要在二方包里將對(duì)外暴露的bean定義在spring.factories
中就好了。對(duì)于不需要的bean,可以在使用方用@EnableAutoConfiguration
的exclude
屬性進(jìn)行排除。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。