深入了解Java SpringBoot自動(dòng)裝配原理
在使用springboot時(shí),很多配置我們都沒有做,都是springboot在幫我們完成,這很大一部分歸功于springboot自動(dòng)裝配,那springboot的自動(dòng)裝配的原理是怎么實(shí)現(xiàn)的呢?
自動(dòng)裝配原理
springboot 版本:2.4.3
SpringBootApplication
springboot啟動(dòng)類必須要加@SpringBootApplication注解,那這個(gè)注解是什么意思呢?
@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 @interface SpringBootApplication {
拋開元數(shù)據(jù)注解來說,SpringBootApplication注解主要由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan組成。這三個(gè)又有不同的作用如下:
- @SpringBootConfiguration:被@Configuration標(biāo)記,表示這是個(gè)springboot配置,支持JavaConfig的方式來進(jìn)行配置。
- @EnableAutoConfiguration:表示開啟自動(dòng)裝配(重點(diǎn)介紹)
- @ComponentScan:掃描注解,掃描basePackages包下的bean并將他們注入到IOC容器中,比如:@Service、@Controller、@Component等注解。
EnableAutoConfiguration
真正開啟自動(dòng)配置的還是@EnableAutoConfiguration注解,來看下EnableAutoConfiguration注解源碼:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
@EnableAutoConfiguration 又是由@AutoConfigurationPackage和@Import注解組成。
- @AutoConfigurationPackage是一個(gè)復(fù)合注解的,它在內(nèi)部使用@Import(AutoConfigurationPackages.Registrar.class)注解,Registrar是AutoConfigurationPackages的一個(gè)內(nèi)部類,它的作用就是注冊一個(gè)springboot啟動(dòng)類所在的包名,這個(gè)包名可以供列如JPA的使用。
- AutoConfigurationImportSelector通過selectImports方法將配置類導(dǎo)入,從而完成bean的裝配
AutoConfigurationImportSelector
AutoConfigurationImportSelector實(shí)現(xiàn)了DeferredImportSelector接口,DeferredImportSelector是ImportSelector的變種,它是一個(gè)延遲選擇器。實(shí)現(xiàn)了DeferredImportSelector接口的子類如果重新了getImportGroup方法并返回DeferredImportSelector內(nèi)部接口Group的子類,DeferredImportSelector接口的子類的子類將不會(huì)調(diào)用selectImports而是調(diào)用Group的selectImports方法。
接下來看看AutoConfigurationImportSelector重寫了getImportGroup方法并返回一個(gè)內(nèi)部類AutoConfigurationGroup,AutoConfigurationGroup#selectImports方法只是對配置數(shù)組進(jìn)行排序篩選,真正處理自動(dòng)配置的流程的是process方法。
process方法源碼如下:
@Override public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }
process方法就是對AutoConfigurationGroup一些屬性的填充,起作用的還是AutoConfigurationImportSelector.getAutoConfigurationEntry方法。
getAutoConfigurationEntry方法中經(jīng)過各種判斷過濾、去重等操作,最后返回AutoConfigurationEntry對象。源碼如下
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } //獲取EnableAutoConfiguration注解的exclude和excludeName屬性 AnnotationAttributes attributes = getAttributes(annotationMetadata); //獲取所有配置類 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); //去除重復(fù)配置類 configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); //移除 exclude的配置類 configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
getAutoConfigurationEntry 篩選的自動(dòng)配置類:
getAutoConfigurationEntry方法中要重點(diǎn)分析的是getCandidateConfigurations方法。getCandidateConfigurations的作用是獲取所有自動(dòng)裝配的配置類的全限定名。
來看下getCandidateConfigurations方法源碼:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
這里面用到了SpringFactoriesLoader是spring提供的一種加載配置的方式,它會(huì)將類從配置文件中讀取到,然后利用反射將bean加載到IOC容器中。
SpringFactoriesLoader.loadFactoryNames中會(huì)加載META-INF/spring.factories自動(dòng)配置類。這些配置類在spring.factories文件中是以key=value的形式存儲(chǔ)的,來看下部分自動(dòng)配置類:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
來看下SpringFactoriesLoader.loadFactoryNames源碼:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } return result; }
loadFactoryNames方法只是傳遞一個(gè)org.springframework.boot.autoconfigure.EnableAutoConfiguration作為key,然后取到對應(yīng)的自動(dòng)配置類列表。
最終調(diào)用的是loadSpringFactories方法,loadSpringFactories會(huì)從jar包中找到spring.factories文件然后將其中的自動(dòng)配置類存到一個(gè)map中,從下圖可以看到map中存在很多bean,loadFactoryNames方法在加載自動(dòng)配置類時(shí)只取了一個(gè)key。弱水三千,只取一瓢。
loadSpringFactories返回結(jié)果:
自動(dòng)裝配流程圖大致如下:
總結(jié)
EnableAutoConfiguration注解開啟自動(dòng)裝配,其上的標(biāo)記的@Import(AutoConfigurationImportSelector.class)注解中導(dǎo)入配置類
AutoConfigurationImportSelector實(shí)現(xiàn)DeferredImportSelector接口,并重寫了getImportGroup方法并返回AutoConfigurationImportSelector.AutoConfigurationGroup,AutoConfigurationImportSelector.AutoConfigurationGroup.process開始處理自動(dòng)配置流程。
AutoConfigurationImportSelector.getCandidateConfigurations獲取所有配置類getAutoConfigurationEntry方法篩選,去重、移除不符合條件的自動(dòng)配置類。
SpringFactoriesLoader.loadSpringFactories從jar包中找到所有META-INF/spring.factories文件并讀取自動(dòng)配置類,存放到map中, loadFactoryNames方法通過全限定名org.springframework.boot.autoconfigure.EnableAutoConfiguration找到自動(dòng)配置類。
最后經(jīng)過層層篩選,去重、移除不符合條件的bean,由ConfigurationClassPostProcessor#processConfigBeanDefinitions注冊所有的自動(dòng)配置類。
到此這篇關(guān)于深入了解Java SpringBoot自動(dòng)裝配原理的文章就介紹到這了,更多相關(guān)SpringBoot自動(dòng)裝配內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java生成json實(shí)現(xiàn)隱藏掉關(guān)鍵屬性
這篇文章主要介紹了java生成json實(shí)現(xiàn)隱藏掉關(guān)鍵屬性,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03基于OAuth2.0授權(quán)系統(tǒng)的驗(yàn)證碼功能的實(shí)現(xiàn)
本篇教程給大家分享基于OAuth2.0授權(quán)系統(tǒng)的驗(yàn)證碼功能的實(shí)現(xiàn),驗(yàn)證碼功能的實(shí)現(xiàn)是采用Zuul網(wǎng)關(guān)的Filter過濾器進(jìn)行校驗(yàn)驗(yàn)證碼,具體實(shí)現(xiàn)代碼跟隨小編一起看看吧2021-05-05java如何利用FastJSON、Gson、Jackson三種Json格式工具自定義時(shí)間序列化
本篇文章主要介紹了java如何利用FastJSON、Gson、Jackson三種Json格式工具自定義時(shí)間序列化,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08在SpringBoot項(xiàng)目中的使用Swagger的方法示例
這篇文章主要介紹了在SpringBoot項(xiàng)目中的使用Swagger的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05Springboot項(xiàng)目啟動(dòng)成功后可通過五種方式繼續(xù)執(zhí)行
本文主要介紹了Springboot項(xiàng)目啟動(dòng)成功后可通過五種方式繼續(xù)執(zhí)行,主要包括CommandLineRunner接口,ApplicationRunner接口,ApplicationListener接口,@PostConstruct注解,InitalizingBean接口,感興趣的可以了解一下2023-12-12