深入了解Java SpringBoot自動裝配原理
在使用springboot時,很多配置我們都沒有做,都是springboot在幫我們完成,這很大一部分歸功于springboot自動裝配,那springboot的自動裝配的原理是怎么實現(xiàn)的呢?
自動裝配原理
springboot 版本:2.4.3
SpringBootApplication
springboot啟動類必須要加@SpringBootApplication注解,那這個注解是什么意思呢?
@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組成。這三個又有不同的作用如下:
- @SpringBootConfiguration:被@Configuration標(biāo)記,表示這是個springboot配置,支持JavaConfig的方式來進行配置。
- @EnableAutoConfiguration:表示開啟自動裝配(重點介紹)
- @ComponentScan:掃描注解,掃描basePackages包下的bean并將他們注入到IOC容器中,比如:@Service、@Controller、@Component等注解。
EnableAutoConfiguration
真正開啟自動配置的還是@EnableAutoConfiguration注解,來看下EnableAutoConfiguration注解源碼:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
@EnableAutoConfiguration 又是由@AutoConfigurationPackage和@Import注解組成。
- @AutoConfigurationPackage是一個復(fù)合注解的,它在內(nèi)部使用@Import(AutoConfigurationPackages.Registrar.class)注解,Registrar是AutoConfigurationPackages的一個內(nèi)部類,它的作用就是注冊一個springboot啟動類所在的包名,這個包名可以供列如JPA的使用。
- AutoConfigurationImportSelector通過selectImports方法將配置類導(dǎo)入,從而完成bean的裝配
AutoConfigurationImportSelector
AutoConfigurationImportSelector實現(xiàn)了DeferredImportSelector接口,DeferredImportSelector是ImportSelector的變種,它是一個延遲選擇器。實現(xiàn)了DeferredImportSelector接口的子類如果重新了getImportGroup方法并返回DeferredImportSelector內(nèi)部接口Group的子類,DeferredImportSelector接口的子類的子類將不會調(diào)用selectImports而是調(diào)用Group的selectImports方法。
接下來看看AutoConfigurationImportSelector重寫了getImportGroup方法并返回一個內(nèi)部類AutoConfigurationGroup,AutoConfigurationGroup#selectImports方法只是對配置數(shù)組進行排序篩選,真正處理自動配置的流程的是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 篩選的自動配置類:
getAutoConfigurationEntry方法中要重點分析的是getCandidateConfigurations方法。getCandidateConfigurations的作用是獲取所有自動裝配的配置類的全限定名。
來看下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提供的一種加載配置的方式,它會將類從配置文件中讀取到,然后利用反射將bean加載到IOC容器中。
SpringFactoriesLoader.loadFactoryNames中會加載META-INF/spring.factories自動配置類。這些配置類在spring.factories文件中是以key=value的形式存儲的,來看下部分自動配置類:
# 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方法只是傳遞一個org.springframework.boot.autoconfigure.EnableAutoConfiguration作為key,然后取到對應(yīng)的自動配置類列表。
最終調(diào)用的是loadSpringFactories方法,loadSpringFactories會從jar包中找到spring.factories文件然后將其中的自動配置類存到一個map中,從下圖可以看到map中存在很多bean,loadFactoryNames方法在加載自動配置類時只取了一個key。弱水三千,只取一瓢。
loadSpringFactories返回結(jié)果:
自動裝配流程圖大致如下:
總結(jié)
EnableAutoConfiguration注解開啟自動裝配,其上的標(biāo)記的@Import(AutoConfigurationImportSelector.class)注解中導(dǎo)入配置類
AutoConfigurationImportSelector實現(xiàn)DeferredImportSelector接口,并重寫了getImportGroup方法并返回AutoConfigurationImportSelector.AutoConfigurationGroup,AutoConfigurationImportSelector.AutoConfigurationGroup.process開始處理自動配置流程。
AutoConfigurationImportSelector.getCandidateConfigurations獲取所有配置類getAutoConfigurationEntry方法篩選,去重、移除不符合條件的自動配置類。
SpringFactoriesLoader.loadSpringFactories從jar包中找到所有META-INF/spring.factories文件并讀取自動配置類,存放到map中, loadFactoryNames方法通過全限定名org.springframework.boot.autoconfigure.EnableAutoConfiguration找到自動配置類。
最后經(jīng)過層層篩選,去重、移除不符合條件的bean,由ConfigurationClassPostProcessor#processConfigBeanDefinitions注冊所有的自動配置類。
到此這篇關(guān)于深入了解Java SpringBoot自動裝配原理的文章就介紹到這了,更多相關(guān)SpringBoot自動裝配內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java生成json實現(xiàn)隱藏掉關(guān)鍵屬性
這篇文章主要介紹了java生成json實現(xiàn)隱藏掉關(guān)鍵屬性,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03基于OAuth2.0授權(quán)系統(tǒng)的驗證碼功能的實現(xiàn)
本篇教程給大家分享基于OAuth2.0授權(quán)系統(tǒng)的驗證碼功能的實現(xiàn),驗證碼功能的實現(xiàn)是采用Zuul網(wǎng)關(guān)的Filter過濾器進行校驗驗證碼,具體實現(xiàn)代碼跟隨小編一起看看吧2021-05-05java如何利用FastJSON、Gson、Jackson三種Json格式工具自定義時間序列化
本篇文章主要介紹了java如何利用FastJSON、Gson、Jackson三種Json格式工具自定義時間序列化,具有一定的參考價值,有興趣的可以了解一下2017-08-08Springboot項目啟動成功后可通過五種方式繼續(xù)執(zhí)行
本文主要介紹了Springboot項目啟動成功后可通過五種方式繼續(xù)執(zhí)行,主要包括CommandLineRunner接口,ApplicationRunner接口,ApplicationListener接口,@PostConstruct注解,InitalizingBean接口,感興趣的可以了解一下2023-12-12