關(guān)于SpringBoot的自動(dòng)裝配原理詳解
一、@SpringBootApplication
正常情況下的啟動(dòng)類都會(huì)加上@SpringBootApplication注解
@SpringBootApplication public class SpringbootSourceApplication { public static void main(String[] args) { SpringApplication.run(SpringbootSourceApplication.class, args); } }
我們查看下@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) }) @ConfigurationPropertiesScan public @interface SpringBootApplication { }
我們可以看到有@EnableAutoConfiguration注解,這個(gè)是自動(dòng)裝配的核心
二、@EnableAutoConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { }
其中有兩個(gè)注解
@AutoConfigurationPackage
這個(gè)注解的作用就是將注解標(biāo)記的類所在包及所有子包下的組件到掃描到Spring容器中
@Import(AutoConfigurationImportSelector.class)
- @Import根據(jù)配置內(nèi)容有三種使用方式
- class數(shù)組
- 將數(shù)組內(nèi)的類注入到Spring容器中,bean名稱是全類名
ImportSelector
類型- 實(shí)現(xiàn)
ImportSelector
接口
- 實(shí)現(xiàn)
- class數(shù)組
- @Import根據(jù)配置內(nèi)容有三種使用方式
public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */ String[] selectImports(AnnotationMetadata importingClassMetadata); }
返回值:就是我們實(shí)際需要注入到容器的組件全類名(返回值可以是空數(shù)組,但是不能為null,否則會(huì)有空指針異常)
參數(shù):當(dāng)前被@Import注解標(biāo)記的所有注解信息
ImportBeanDefinitionRegistrar
類型
實(shí)現(xiàn)ImportBeanDefinitionRegistrar
接口,例如
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { //指定bean定義信息(包括bean的類型、作用域...) RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(自己的類.class); //注冊(cè)一個(gè)bean指定bean名字(id) beanDefinitionRegistry.registerBeanDefinition("自定義名稱",rootBeanDefinition); } }
可以自定義一個(gè)或多個(gè)bean
這里采用的是第二種方式,通過AutoConfigurationImportSelector
來返回需要注入的類名數(shù)組
三、AutoConfigurationImportSelector
我們看下AutoConfigurationImportSelector
類中selectImports
方法
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } //1、加載spring-autoconfigure-metadata.properties信息,并保存到AutoConfigurationMetadata實(shí)例中 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); //2、獲取自動(dòng)裝配信息 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
這個(gè)方法有兩步
- 第一步:加載
spring-autoconfigure-metadata.properties
信息,并保存到AutoConfigurationMetadata
實(shí)例中 - 第二步:獲取自動(dòng)裝配信息
1、加載spring-autoconfigure-metadata.properties信息
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); } static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { try { Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path); Properties properties = new Properties(); while (urls.hasMoreElements()) { properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement()))); } return loadMetadata(properties); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex); } }
邏輯很簡(jiǎn)單,就是加載spring-autoconfigure-metadata.properties
并封裝到AutoConfigurationMetadata
中。spring-autoconfigure-metadata.properties
存儲(chǔ)了自動(dòng)裝配的條件元信息,例如
#...省略... org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration.ConditionalOnWebApplication=SERVLET org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration.ConditionalOnBean=javax.jms.ConnectionFactory #...省略...
格式:全類名.條件=條件值
條件(列舉下面三個(gè)常見的)
ConditionalOnClass
:classpath
下存在某個(gè)類才會(huì)加載ConditionalOnBean
:容器內(nèi)下存在某個(gè)bean才會(huì)加載ConditionalOnWebApplication
:在什么樣的web環(huán)境下才會(huì)加載
2、獲取自動(dòng)裝配的類信息
這里的消息包括兩個(gè):最終需要自動(dòng)裝配的類列表,和被排除的類列表
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { //1、判斷是否允許自動(dòng)裝配,如果否則直接返回空對(duì)象 if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } //2、獲取@EnableAutoConfiguration注解上標(biāo)注的類的元信息 AnnotationAttributes attributes = getAttributes(annotationMetadata); //3、獲取自動(dòng)裝配的候選類名集合 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); //4、可能存在重復(fù),所以這里進(jìn)行去重(因?yàn)槭菑呐渲梦募惷@取的,可能會(huì)配置重,所有先去下重) configurations = removeDuplicates(configurations); //5、獲取自動(dòng)裝配組件的排除名單 Set<String> exclusions = getExclusions(annotationMetadata, attributes); //6、檢查排除名單是否合法 checkExcludedClasses(configurations, exclusions); //7、排除exclusions中的類 configurations.removeAll(exclusions); //8、執(zhí)行過濾操作,依賴前面AutoConfigurationMetadataLoader.loadMetadata獲取的autoConfigurationMetadata configurations = filter(configurations, autoConfigurationMetadata); //9、觸發(fā)自動(dòng)裝配的導(dǎo)入事件 fireAutoConfigurationImportEvents(configurations, exclusions); //10、返回信息 return new AutoConfigurationEntry(configurations, exclusions); }
分為一下10個(gè)步驟
2.1)判斷是否允許自動(dòng)裝配,如果否則直接返回空對(duì)象
//1、判斷是否允許自動(dòng)裝配,如果否則直接返回空對(duì)象 if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; }
protected boolean isEnabled(AnnotationMetadata metadata) { if (getClass() == AutoConfigurationImportSelector.class) { return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true); } return true; }
如果是AutoConfigurationImportSelector
類型,則需要判斷配置中的spring.boot.enableautoconfiguration
是否為true,否則直接返回空對(duì)象
2.2)獲取@EnableAutoConfiguration
注解上標(biāo)注的類的元信息
//2、獲取@EnableAutoConfiguration注解上標(biāo)注的類的元信息 AnnotationAttributes attributes = getAttributes(annotationMetadata);
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) { String name = getAnnotationClass().getName(); AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true)); Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?"); return attributes; }
獲取注解上的元信息
2.3)獲取自動(dòng)裝配的候選類名集合
//3、獲取自動(dòng)裝配的候選類名集合 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
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; }
核心方法是SpringFactoriesLoader
中的loadFactoryNames
方法
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { 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); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
搜索指定ClassLoader
下所有的META/spring.factories
資源內(nèi)容,可能會(huì)返回多個(gè)
spring.factories
中存放了所有自動(dòng)裝配的候選類,這些類名格式都是XXXAutoConfiguration
# 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.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ ......
將這些資源內(nèi)容作為Properties
文件讀取,合并為一個(gè)Key
為接口的全類名,Value
是實(shí)現(xiàn)全類名列表的Map,作為loadSpringFactories
的返回值
再?gòu)纳弦徊椒祷氐腗ap中查找并返回方法指定類名所映射的實(shí)現(xiàn)類全類名列表
2.4)去重
//4、可能存在重復(fù),所以這里進(jìn)行去重(因?yàn)槭菑呐渲梦募惷@取的,可能會(huì)配置重,所有先去下重) configurations = removeDuplicates(configurations);
因?yàn)榕渲梦募赡艽嬖谥貜?fù),因此這里手動(dòng)做了去重處理
protected final <T> List<T> removeDuplicates(List<T> list) { return new ArrayList<>(new LinkedHashSet<>(list)); }
使用到LinkedHashSet
來去重
2.5)獲取自動(dòng)裝配組件的排除名單
//5、獲取自動(dòng)裝配組件的排除名單 Set<String> exclusions = getExclusions(annotationMetadata, attributes);
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { Set<String> excluded = new LinkedHashSet<>(); excluded.addAll(asList(attributes, "exclude")); excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; } private List<String> getExcludeAutoConfigurationsProperty() { if (getEnvironment() instanceof ConfigurableEnvironment) { Binder binder = Binder.get(getEnvironment()); return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList) .orElse(Collections.emptyList()); } String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class); return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList(); }
排除名單來源于三個(gè)地方
@EnableAutoConfiguration
注解中的exclue
屬性@EnableAutoConfiguration
注解中的excludeName
屬性spring.autoconfigure.exclude
配置
2.6)檢查排除名單是否合法
//6、檢查排除名單是否合法 checkExcludedClasses(configurations, exclusions);
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) { List<String> invalidExcludes = new ArrayList<>(exclusions.size()); for (String exclusion : exclusions) { if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) { invalidExcludes.add(exclusion); } } if (!invalidExcludes.isEmpty()) { handleInvalidExcludes(invalidExcludes); } } protected void handleInvalidExcludes(List<String> invalidExcludes) { StringBuilder message = new StringBuilder(); for (String exclude : invalidExcludes) { message.append("\t- ").append(exclude).append(String.format("%n")); } throw new IllegalStateException(String.format( "The following classes could not be excluded because they are not auto-configuration classes:%n%s", message)); }
當(dāng)排除類存在于當(dāng)前ClassLoader
,且不在自動(dòng)裝配的候選類名單上,則當(dāng)前排除類非法,會(huì)觸發(fā)IllegalStateException
異常
2.7)排除exclusions中的類
//7、排除exclusions中的類 configurations.removeAll(exclusions);
2.8)執(zhí)行過濾操作
//8、執(zhí)行過濾操作,依賴前面AutoConfigurationMetadataLoader.loadMetadata獲取的autoConfigurationMetadata configurations = filter(configurations, autoConfigurationMetadata);
- 第一個(gè)參數(shù):自動(dòng)裝配候選名單
- 第二個(gè)參數(shù):自動(dòng)裝配的條件元信息
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) { long startTime = System.nanoTime(); //將候選名單轉(zhuǎn)化為數(shù)組,目的是為了下面的參數(shù)匹配 String[] candidates = StringUtils.toStringArray(configurations); //每個(gè)位置都標(biāo)記是否需要過濾 boolean[] skip = new boolean[candidates.length]; //是否發(fā)生過過濾,目的是如果沒有發(fā)生過,則不需要遍歷skip數(shù)組進(jìn)行篩選 boolean skipped = false; //獲取當(dāng)前beanClassLoader下的所有AutoConfigurationImportFilter for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { invokeAwareMethods(filter); //匹配計(jì)算出結(jié)果 boolean[] match = filter.match(candidates, autoConfigurationMetadata); //根據(jù)結(jié)果更新skip for (int i = 0; i < match.length; i++) { //如果不匹配,則表示當(dāng)前位置的類不滿足條件,需要過濾掉 if (!match[i]) { skip[i] = true; //這里是手動(dòng)釋放引用,方便下次GC回收 candidates[i] = null; skipped = true; } } } if (!skipped) { return configurations; } //過濾出需要加載的類 List<String> result = new ArrayList<>(candidates.length); for (int i = 0; i < candidates.length; i++) { if (!skip[i]) { result.add(candidates[i]); } } if (logger.isTraceEnabled()) { int numberFiltered = configurations.size() - result.size(); logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms"); } return new ArrayList<>(result); } //獲取當(dāng)前beanClassLoader下的所有AutoConfigurationImportFilter protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); }
該方法核心就是根據(jù)spring-autoconfigure-metadata.properties
中配置的條件進(jìn)行候選名單的過濾,目的是減少不必要的類加載,提高啟動(dòng)速度
2.9)觸發(fā)自動(dòng)裝配的導(dǎo)入事件
//9、觸發(fā)自動(dòng)裝配的導(dǎo)入事件 fireAutoConfigurationImportEvents(configurations, exclusions);
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) { List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners(); if (!listeners.isEmpty()) { AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions); for (AutoConfigurationImportListener listener : listeners) { invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event); } } } protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader); }
獲取當(dāng)前beanClassLoader
下的所有的AutoConfigurationImportListener
實(shí)例,執(zhí)行對(duì)應(yīng)的監(jiān)聽方法
2.10)返回自動(dòng)裝配信息
//10、返回信息 return new AutoConfigurationEntry(configurations, exclusions);
返回的信息內(nèi)容
List<String> configurations
:最終需要自動(dòng)裝配的類列表Set<String> exclusions
:排除名單
四、總結(jié)(自動(dòng)裝配流程)
- 查詢配置spring.boot.enableautoconfiguration,如果是true則繼續(xù),否則表示不啟用自動(dòng)裝配,直接返回空對(duì)象
- 讀取所有META-INF/spring-autoconfigure-metadata.properties資源,保存為自動(dòng)裝配的條件元信息,后續(xù)用來做最后的過濾
- 讀取所有META-INF/spring.factories資源中@EnableAutoConfiguration所關(guān)聯(lián)的自動(dòng)裝配Class集合
- 讀取當(dāng)前配置類所標(biāo)注的@EnableAutoConfiguration屬性exclude和excludeName,以及spring.autoconfigure.exclude配置屬性合并為自動(dòng)裝配Class排除集合
- 檢查自動(dòng)裝配Class排除集合是否合法
- 排除候選自動(dòng)裝配Class集合中的排除名單
- 使用之前加載的條件元信息,再次過濾候選自動(dòng)裝配Class集合中Class不存在的成員
- 自動(dòng)裝配Class集合過濾完成后,觸發(fā)AutoConfigurationImportEvent監(jiān)聽器執(zhí)行
- 返回裝配Class集合+排除名單
到此這篇關(guān)于關(guān)于SpringBoot的自動(dòng)裝配原理詳解的文章就介紹到這了,更多相關(guān)SpringBoot的自動(dòng)裝配內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Validator從零掌握對(duì)象校驗(yàn)的詳細(xì)過程
SpringValidator學(xué)習(xí)指南從零掌握對(duì)象校驗(yàn),涵蓋Validator接口、嵌套對(duì)象處理、錯(cuò)誤代碼解析等核心概念,幫助開發(fā)者實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)的規(guī)范與高效,本文詳細(xì)介紹Spring Validator從零掌握對(duì)象校驗(yàn),感興趣的朋友一起看看吧2025-02-02springboot實(shí)現(xiàn)用戶名查找用戶功能
本文主要介紹了springboot實(shí)現(xiàn)用戶名查找用戶功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04java求數(shù)組元素重復(fù)次數(shù)和java字符串比較大小示例
這篇文章主要介紹了java求數(shù)組元素重復(fù)次數(shù)和java字符串比較大小示例,需要的朋友可以參考下2014-04-04Java實(shí)現(xiàn)簡(jiǎn)單樹結(jié)構(gòu)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單樹結(jié)構(gòu)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01IDEA神器一鍵查看Java字節(jié)碼及其他類信息插件
這篇文章主要為大家介紹了一款I(lǐng)DEA神器,可以一鍵查看Java字節(jié)碼及其他類信息,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-01-01Intellij IDEA菜單欄不見了(Main Menu as Separat
有人問博主,關(guān)于Intellij IDEA菜單欄找不到了,被不小心的操作給隱藏了,怎么辦?下面給大家分享解決方案,感興趣的朋友跟隨小編一起看看吧2024-06-06springboot+vue制作后臺(tái)管理系統(tǒng)項(xiàng)目
本文詳細(xì)介紹了后臺(tái)管理使用springboot+vue制作,以分步驟、圖文的形式詳細(xì)講解,大家有需要的可以參考參考2021-08-08