詳解SpringBoot簡化配置分析總結(jié)
在SpringBoot啟動類中,該主類被@SpringBootApplication所修飾,跟蹤該注解類,除元注解外,該注解類被如下自定注解修飾。
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
讓我們簡單敘述下它們各自的功能:
- @ComponentScan:掃描需要被IoC容器管理下需要管理的Bean,默認當前根目錄下的
- @EnableAutoConfiguration:裝載所有第三方的Bean
- @SpringBootConfiguration 作用等同于@Configuration
我們來看下@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
可以看到該注解類內(nèi)包含與@Configuration,其作用與@Configuration并無太大區(qū)別,只是多了層屬性嵌套。
故: @SpringBootConfiguration + @ComponentScan
將根目錄下所有被**@Controller、@Service、@Repository、@Component**等所修飾的類交給IoC容器管理。
那么重點來了,@EnableAutoConfiguration是如何裝載第三方Bean的呢?讓我們跟蹤下它的源碼。
首先我們可以看到該類被如下注解修飾:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
我們先關注下AutoConfigurationImportSelector這個組件。
// 批量導入第三方的一些Bean
@Import({AutoConfigurationImportSelector.class})
其中該組件的selectImports(AnnotationMetadata annotationMetadata)方法,我們先簡述下它的作用:掃描所有需要被管理的第三方Bean并交給IoC容器進行管理。然后我們接著往下追蹤。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 讓我們跟蹤到這個方法
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 獲取所有AutoConfiguration的配置類
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 下面就是對AutoConfiguration的去重、排除和過濾等操作
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
// 我們繼續(xù)追蹤這里
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
// 加了層包裝
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
Iterator var5 = listeners.iterator();
while(var5.hasNext()) {
AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
this.invokeAwareMethods(listener);
// 向ConditionEvaluationReport中導入所有AutoConfiguration
listener.onAutoConfigurationImportEvent(event);
}
}
}
可以猜想IoC容器在啟動時會將這里的AutoConfiguration中的每個Bean都注入到容器中。這里的源碼我們先跟蹤到這里,大致了解了下該方法的作用。
那么SpringBoot又是如何取感知第三方的Bean文件呢?
SpringBoot和第三方Bean之間存在一定的規(guī)定。即通過對于相應依賴的Jar包中可能存在一個spring.factories文件,在該文件中就記錄了需要被IoC容器管理的Bean文件路徑,SpringBoot通過該文件確定需要IoC管理的Bean文件位置。對于spring-boot-autoconfiguration的spring.factories文件中,記錄著大量xxxAutoConfiguration的類文件位置,這些類都被@Configuration注解標識,即這些配置類會配置多個Bean從而解決spring.factories可能產(chǎn)生的臃腫問題。
Tomcat的加載時機
對于SpringBoot來說它特點不僅是簡化配置,還有內(nèi)嵌容器等特點。那么就有必要探討Tomcat容器的加載時機。在spring-boot-autoconfiguration的spring.factories文件中存在ServletWebServerFactoryAutoConfiguration配置類的路徑,該類會在項目啟動時將默認的Tomcat容器已@Bean的方式加載入IoC容器內(nèi)部。
SpringBoot是如何集中配置呢?
談論這個問題前我們不妨先按照之前yml或properties的文件配置下
server: port: 8080
通過IDE,跟蹤到port所配置的成員變量所在類,發(fā)現(xiàn)該類被@ConfigurationProperties所修飾,該注解就是將yml或properties中配置按照對應前綴注入到指定類的成員變量。該注解具體實現(xiàn)感興趣的小伙伴們可以去如下鏈接學習。 @ConfigurationProperties實現(xiàn)原理與實戰(zhàn)
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
*******
}
下面兩個代碼和前述作用大致相同
environment.getProperty("xxx");
@Value("${xxx}")
我們在使用SpringBoot時只需要做哪些事情?
通常我們再使用SpringBoot時只需要在Maven中引入類似如下的starter依賴。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
最多再需要配置一些類似mybatis這類框架的一些屬性參數(shù)。而這些starter按照我們之前的邏輯其內(nèi)部應該存有spring.factories文件,我們先去對應jar包查找下。
如果有些starter的jar包沒有找到我們想要的spring.factories文件。我們可以去spring-boot-test-autoconfiguretion中的spring.factories查看下,SpringBoot內(nèi)部其實已經(jīng)定義好相當一定數(shù)量的AutoConfiguration。

果然該jar包內(nèi)確實存在spring.factories文件,代碼如下。
org.springframework.data.repository.core.support.RepositoryFactorySupport=org.springframework.data.redis.repository.support.RedisRepositoryFactory
這意味著我們已經(jīng)簡單地了解了SpringBoot如何簡化配置,那么我們也應該可以自己來實現(xiàn)一個starter依賴交給SpringBoot來使用,只要在對應Jar包中添加spring.factories文件,在其中添加如下代碼。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxxAutoConfiguration
大家若有時間還請實現(xiàn)下自己的starter依賴,對加深這部分理解還是很有幫助的。感興趣的小伙伴可以看下我做的一個簡單的實現(xiàn)。 [自定義starter實現(xiàn)]
最后我們在說下最后@SpringBootApplication中@AutoConfigurationPackage這個注解類,發(fā)現(xiàn)其中導入了Registrar組件。
@Import({Registrar.class})
讓我們重點關注registerBeanDefinitions這個方法,該方法最終會來到DefaultListableBeanFactory中registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法,將AutoConfigurationPackages.class注冊到IoC容器中,然后將主配置類所在包下所有組件導入到SpringIoC容器中
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 里面就這一個方法我們跟蹤下
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
// 判斷beanDefinitionMap是否存在AutoConfigurationPackages
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(AutoConfigurationPackages.BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(2);
// 將設置好的AutoConfigurationPackages注冊到beanDefinitionMap(是不是很熟悉這一步)
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
怎么樣,在為我們簡化了配置的同時,SpringBoot居然幫我們做了如此多的事情,而我們只需要簡單地集中配置其中一部分的屬性。關于SpirngBoot我們就探討到這里,這些內(nèi)容是閱讀一些文章,觀看部分講解和源碼的總結(jié),若有錯誤還請接納與指教。這是本人的第一篇文章,最后感謝各位的閱讀。
到此這篇關于詳解SpringBoot簡化配置分析總結(jié)的文章就介紹到這了,更多相關SpringBoot 簡化配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java面試題沖刺第十三天--數(shù)據(jù)庫(3)
這篇文章主要為大家分享了最有價值的三道數(shù)據(jù)庫面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-07-07
java 數(shù)據(jù)結(jié)構(gòu)之堆排序(HeapSort)詳解及實例
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)之堆排序(HeapSort)詳解及實例的相關資料,需要的朋友可以參考下2017-03-03
簡單講解Java的Socket網(wǎng)絡編程的多播與廣播實現(xiàn)
這篇文章主要介紹了Java的Socket網(wǎng)絡編程的多播與廣播實現(xiàn),包括網(wǎng)絡編程發(fā)送和接受數(shù)據(jù)的一些基礎知識整理,需要的朋友可以參考下2016-01-01
Spring注解@Qualifier的使用&&與@Primary注解的不同
今天帶你了解一下Spring框架中的@Qualifier?注解,它解決了哪些問題,以及如何使用它,我們還將了解它與?@Primary?注解的不同之處,感興趣的朋友跟隨小編一起看看吧2023-10-10
Spring Cloud Gateway 獲取請求體(Request Body)的多種方法
這篇文章主要介紹了Spring Cloud Gateway 獲取請求體(Request Body)的多種方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01

