SpringBoot中的自動(dòng)配置原理詳解
引言
springboot的自動(dòng)配置類直觀的表現(xiàn)就是:通過一系列的注解,使得springboot項(xiàng)目在啟動(dòng)的時(shí)候從配置文件中加載需要自動(dòng)配置的類。
如果該配置類有引入相關(guān)的jar的文件的時(shí)候,springboot便會(huì)讓對(duì)應(yīng)的類實(shí)例化,注入容器中。
即做到在沒有任何配置的情況下就可直接使用。當(dāng)沒有引入對(duì)應(yīng)的jar文件的時(shí)候springboot便不會(huì)自動(dòng)實(shí)例化配置類。
@SpringBootApplication原理
要想理解其中的原因核心便是圍繞一個(gè)注解@SpringBootApplication
@SpringBootApplication注解
直接上源碼:
@Target(ElementType.TYPE)//表示@SpringBootApplication的使用位置,表示用在類上 //1.CONSTRUCTOR:用于描述構(gòu)造器 //2.FIELD:用于描述域 //3.LOCAL_VARIABLE:用于描述局部變量 //4.METHOD:用于描述方法 //5.PACKAGE:用于描述包 //6.PARAMETER:用于描述參數(shù) //7.TYPE:用于描述類、接口(包括注解類型) 或enum聲明 @Retention(RetentionPolicy.RUNTIME)//表示@SpringBootApplication的存在階段。 //1.RetentionPolicy.SOURCE —— 這種類型的Annotations只在源代碼級(jí)別保留,編譯時(shí)就會(huì)被忽略 //2.RetentionPolicy.CLASS —— 這種類型的Annotations編譯時(shí)被保留,在class文件中存在,但JVM將 會(huì)忽略 //3.RetentionPolicy.RUNTIME —— 這種類型的Annotations將被JVM保留,所以他們能在運(yùn)行時(shí)被JVM或其他使用反射機(jī)制的代碼所讀取和使用. @Documented// “文檔” 注解表明這個(gè)注解應(yīng)該被 javadoc工具記錄 @Inherited//“繼承” 修飾@SpringBootApplication后,那么@SpringBootApplication用在哪個(gè)類上,且該類有子類的話,子類是可以繼承父類中的@SpringBootApplication @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
可以看到其是一個(gè)復(fù)合注解前面的幾個(gè)是元注解并不是核心自動(dòng)配置的原因重點(diǎn)在@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan這三個(gè)注解。
@SpringBootConfiguration注解
源碼:
@Configuration public @interface SpringBootConfiguration {...}
再一層
@Configuration注解
@Component public @interface Configuration {...}
可以看到本質(zhì)是個(gè)@Component,這個(gè)注解很熟悉了吧,再SSM階段直接在spring的xml配置<context:component-scan /> 或者 @ComponentScan,容器啟動(dòng)的時(shí)候會(huì)掃描所有含有@Component的類,通過反射機(jī)制將類的信息加載到內(nèi)存中給IOC使用。
補(bǔ)充@Configuration的使用
1.@Configuration告訴springboot其修飾的類是一個(gè)配置類
@Configuration和@Bean聯(lián)合使用其等價(jià)<bean id='xxx' class='xxx.xxx'>。。。</bean>
@Configuration public class Configuration { // 任何標(biāo)志了@Bean的方法,其返回值將作為一個(gè)bean注冊(cè)到Spring的IOC容器中 // 方法名默認(rèn)成為該bean定義的id @Bean public UserBean user() { return new UserBean(); } }
故再退一步@SpringBootConfiguration注解會(huì)標(biāo)注一個(gè)類是javaConfig,在spring或者springboot啟動(dòng)的時(shí)候會(huì)被掃描。
可以根據(jù)@Bean注解實(shí)例化對(duì)象,作用等于在spring的xml文中定義一個(gè)< bean > < /bean> 。
@EnableAutoConfiguration注解
@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {...}
@Import(EnableAutoConfigurationImportSelector.class)
可以看到其使用了@Import注解:本質(zhì)是為了引入類EnableAutoConfigurationImportSelector類,進(jìn)入其父類AutoConfigurationImportSelector的selectImports()方法,核心代碼如下:
// 從配置文件"META-INF/spring-autoconfigure-metadata.properties"中加載 AutoConfigurationMetadata AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); //從注解中加載exclude和excludeName AnnotationAttributes attributes = getAttributes(annotationMetadata); //從所有jar包下的/META-INF/spring.factories的文件中獲取到候選的自動(dòng)配置類 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//重中之重 configurations = removeDuplicates(configurations); configurations = sort(configurations, autoConfigurationMetadata); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return configurations.toArray(new String[configurations.size()]);
關(guān)于META-INF/spring.factories其再spring-boot-autoconfigure-1.5.9.RELEASE.jar下
@AutoConfigurationPackage注解
@Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {}
@Import(AutoConfigurationPackages.Registrar.class)
它通過將Registrar類導(dǎo)入到容器中,而Registrar類作用是掃描主配置類同級(jí)目錄以及子包,并將相應(yīng)的組件導(dǎo)入到springboot創(chuàng)建管理的容器中;
我們實(shí)際上將我們自己的包下的組件注入容器依賴的還是@AutoConfigurationPackage注解
public static void register(BeanDefinitionRegistry registry, String... packageNames) { if (registry.containsBeanDefinition(BEAN)) { BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments = beanDefinition .getConstructorArgumentValues(); constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames)); } else { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(BasePackages.class); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //ioc registry.registerBeanDefinition(BEAN, beanDefinition); } }
去調(diào)用DefaultListableBeanFactory.java里的registerBeanDefinition()方法
// Cannot modify startup-time collection elements anymore (for stable iteration) //無法再修改啟動(dòng)時(shí)集合元素(用于穩(wěn)定的迭代) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); 。。。。。 }
將已經(jīng)創(chuàng)建好的Bean對(duì)象注入beanDefinitionMap中
@ComponentScan注解
是暫時(shí)將這些符合類型的組件先排除。
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
basePackages、value:指定掃描路徑,如果為空則以@ComponentScan注解的類所在的包為基本的掃描路徑 basePackageClasses:指定具體掃描的類 includeFilters:指定滿足Filter條件的類 excludeFilters:指定排除Filter條件的類
注意:@ComponentScan是可以指定包的掃描, 但是在@SpringBootApplication這個(gè)注解里的@ComponentScan是用來排除主鍵的。
@Conditional注解原理
引入相關(guān)的jar的文件的時(shí)候,springboot便會(huì)讓對(duì)應(yīng)的類實(shí)例化
@Conditional注解
條件裝配:滿足Conditional指定的條件,則進(jìn)行組件注入
@ConditionalOnBean()
當(dāng)其內(nèi)配置的Bean已經(jīng)在容器里的時(shí)候其修飾的類才會(huì)被加載。如何和@Bean合用只有IOC容器里有指定的類的時(shí)候@Bean修飾的方法所創(chuàng)建的實(shí)例才會(huì)被注入IOC容器。
@ConditionalOnMissingBean()
僅當(dāng)指定的Bean類和/或名稱尚未包含在BeanFactory中時(shí),此條件才匹配。如何和@Bean合用只有ioc容器里沒有指定的類的時(shí)候@Bean修飾的方法所創(chuàng)建的實(shí)例才會(huì)被注入IOC容器。
@ConditionalOnMissingClass()
僅當(dāng)指定的類不在類路徑上時(shí)才匹配的條件。
@ConditionalOnWebApplication()
僅當(dāng)應(yīng)用程序上下文是Web應(yīng)用程序上下文時(shí)才匹配的條件。
到此這篇關(guān)于SpringBoot中的自動(dòng)配置原理詳解的文章就介紹到這了,更多相關(guān)SpringBoot自動(dòng)配置原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中char對(duì)應(yīng)的ASCII碼的轉(zhuǎn)化操作
這篇文章主要介紹了java中char對(duì)應(yīng)的ASCII碼的轉(zhuǎn)化操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08Spring MVC整合Shiro權(quán)限控制的方法
這篇文章主要介紹了Spring MVC整合Shiro權(quán)限控制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05如何在Mac上安裝并配置JDK環(huán)境變量詳細(xì)步驟
這篇文章主要介紹了如何在Mac上安裝并配置JDK環(huán)境變量詳細(xì)步驟,包括下載JDK、安裝JDK、配置環(huán)境變量、驗(yàn)證JDK配置以及可選地設(shè)置PowerShell為默認(rèn)終端,需要的朋友可以參考下2025-04-04Java快速排序的實(shí)現(xiàn)詳細(xì)代碼及通俗解釋
這篇文章主要介紹了Java快速排序?qū)崿F(xiàn)的相關(guān)資料,快速排序是一種高效的排序算法,通過選擇一個(gè)基準(zhǔn)值將數(shù)組分成兩部分,左邊的元素比基準(zhǔn)值小,右邊的元素比基準(zhǔn)值大,然后遞歸地對(duì)這兩部分進(jìn)行排序,需要的朋友可以參考下2025-02-02Tomcat能起開,但是訪問不進(jìn)8080首頁的問題解決方案
這篇文章主要介紹了Tomcat能起開,但是訪問不進(jìn)8080首頁的問題解決方案的相關(guān)資料,需要的朋友可以參考下2016-10-10使用SpringBoot設(shè)置虛擬路徑映射絕對(duì)路徑
這篇文章主要介紹了使用SpringBoot設(shè)置虛擬路徑映射絕對(duì)路徑的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08spring boot整合RabbitMQ實(shí)例詳解(Fanout模式)
這篇文章主要介紹了spring boot整合RabbitMQ的實(shí)例講解(Fanout模式),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-04-04