springboot自動(dòng)配置原理解析
前言
小伙伴們都知道,現(xiàn)在市面上最流行的web開發(fā)框架就是springboot了,在springboot開始流行之前,我們都用的是strust2或者是springmvc框架來開發(fā)web應(yīng)用,但是這兩個(gè)框架都有一個(gè)特點(diǎn)就是配置非常的繁瑣,要寫一大堆的配置文件,spring在支持了注解開發(fā)之后稍微有些改觀但有的時(shí)候還是會覺得比較麻煩,這個(gè)時(shí)候springboot就體現(xiàn)出了它的優(yōu)勢,springboot只需要一個(gè)properties或者yml文件就可以簡化springmvc中在xml中需要配置的一大堆的bean,這就是因?yàn)閟pringboot有自動(dòng)配置,那么springboot自動(dòng)配置的原理是什么呢,今天我們就來通過源碼分析一下springboot的自動(dòng)配置原理
開始
我以springboot整合redis為例,來向大家分析springboot的自動(dòng)配置原理
首先創(chuàng)建一個(gè)springboot工程用來測試,然后在pom文件中引入springboot-starter-redis的啟動(dòng)器依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
</dependencies>
然后,在application.properties中配置redis屬性
spring.redis.port=6379 spring.redis.host=localhost spring.redis.database=0
然后,在啟動(dòng)類中注入redisTemplate類,redisTemplate為spring官方提供的對redis底層開發(fā)包(例如jedis)進(jìn)行了深度封裝的組件,使用redisTemplate可以優(yōu)雅的操作redis。我在啟動(dòng)類中寫了一個(gè)測試方法,向redis寫入一條數(shù)據(jù)
@RequestMapping("/redistest")
public String test(){
redisTemplate.opsForSet().add("aaaaa","123456");
return "OK";
}
運(yùn)行這個(gè)方法,打開redis客戶端可以看到值已經(jīng)寫入了

先拋開這里的鍵和值讓人看不懂的問題,大家是不是覺得springboot整合redis要比普通的springmvc整合redis簡單多了?我只配置了redis的連接地址,端口號,注入了redisTemplate,就能開始操作redis了,那么springboot底層到底做了些什么使得整合變得如此的簡單了呢。
首先我們來看,springboot啟動(dòng)類上都有一個(gè)@SpringbootApplication注解,那么這個(gè)注解是起什么作用的呢,讓我們點(diǎn)進(jìn)去看一下
@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
可以看到SpringbootApplication這個(gè)注解是由一系列的注解組合而成,這其中最重要的是@EnableAutoConfiguration和@ComponentScan,@ComponentScan的意思就是組件掃描注解,這個(gè)注解會自動(dòng)注入所有在主程序所在包下的組件。比@ComponentScan注解更重要的就是@EnableAutoConfiguration注解了,這個(gè)注解的含義就是開啟自動(dòng)裝配,直接把bean裝配到ioc容器中,@EnableAutoConfiguration也是一個(gè)組合注解,我們點(diǎn)進(jìn)去看一下
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration
這個(gè)地方我們主要看@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)兩個(gè)注解,首先來看@AutoConfigurationPackage注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
這個(gè)注解主要是獲取我們注解所在包下的組件去進(jìn)行注冊,大家看到這個(gè)@Import注解,那么這個(gè)注解是什么含義呢,
@Import注解用來導(dǎo)入@Configuration注解的配置類、聲明@Bean注解的bean方法、導(dǎo)入ImportSelector的實(shí)現(xiàn)類或?qū)隝mportBeanDefinitionRegistrar的實(shí)現(xiàn)類,這里這個(gè)AutoConfigurationPackages.Registrar.class就是ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類,來看下源碼
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
//metadata是注解的元信息 registry是bean定義的注冊器
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//把注解所在的包下所有的組件都進(jìn)行注冊
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//首先判斷這個(gè)bean有沒有被注冊
if (registry.containsBeanDefinition(BEAN)) {
//獲取bean定義
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
//通過bean定義獲取構(gòu)造函數(shù)值
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
//給構(gòu)造函數(shù)添加參數(shù)值
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
//一個(gè)新的bean定義
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
//設(shè)置beanClass為beanPackages類型
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//bean注冊
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
接下來就是@Import(AutoConfigurationImportSelector.class)這個(gè)注解,我們來看看AutoConfigurationImportSelector這個(gè)類,這個(gè)類是我們自動(dòng)裝配的導(dǎo)入選擇器,首先看這個(gè)類的第一個(gè)方法,其實(shí)也就是這個(gè)類的核心方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//加載元數(shù)據(jù)
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//獲得自動(dòng)裝配的實(shí)體
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//獲得屬性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//獲得候選的配置類,核心方法
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//去除重復(fù)
configurations = removeDuplicates(configurations);
//獲得排除的配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//檢查排除的配置
checkExcludedClasses(configurations, exclusions);
//排除
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
在這部分中,核心方法是getCandidateConfigurations,我們來看下這個(gè)方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//從工廠中獲取自動(dòng)配置類
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
//這句斷言很重要,告訴了我們工廠是去哪里找自動(dòng)配置類的,這里顯然META-INF/spring.factories是一個(gè)路徑
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;
}
那我們就找一下這個(gè)路徑,去哪里找呢,我們看到這個(gè)類的包是org.springframework.boot.autoconfigure;那我們就到這個(gè)包的位置去找這個(gè)spring.factories,果不其然,我們點(diǎn)開這個(gè)文件


我們看到文件中有一行注釋這Auto configure,表示這些都是自動(dòng)配置相關(guān)的類,這里我們不得不說spring框架真的是強(qiáng)大,這里面居然有100多個(gè)自動(dòng)配置類,我們找到redis有關(guān)的自動(dòng)配置類
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
這里我們需要的肯定是第一個(gè)自動(dòng)配置類,我們點(diǎn)進(jìn)去看看
@Configuration
//條件注解,某個(gè)class位于類路徑上,才會實(shí)例化一個(gè)Bean,這個(gè)類是redis操作的類
@ConditionalOnClass(RedisOperations.class)
//使得@ConfigurationProperties 注解的類生效,這個(gè)類是配置redis屬性的類
@EnableConfigurationProperties(RedisProperties.class)
//導(dǎo)入一些配置
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
//僅僅在當(dāng)前上下文中不存在某個(gè)對象時(shí),才會實(shí)例化一個(gè)Bean,這個(gè)就是spring默認(rèn)的redisTemplate
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
我們在application.properties中配置的redis屬性,其實(shí)就是設(shè)置到了這個(gè)類中
//前綴spring.redis
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
/**
* Database index used by the connection factory.
*/
private int database = 0;
/**
* Connection URL. Overrides host, port, and password. User is ignored. Example:
* redis://user:password@example.com:6379
*/
private String url;
/**
* Redis server host.
*/
private String host = "localhost";
/**
* Login password of the redis server.
*/
private String password;
/**
* Redis server port.
*/
private int port = 6379;
/**
* Whether to enable SSL support.
*/
private boolean ssl;
/**
* Connection timeout.
*/
private Duration timeout;
private Sentinel sentinel;
private Cluster cluster;
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
}
我們前面說了,用了spring默認(rèn)的redisTemplate操作redis的話,存到redis里的數(shù)據(jù)對我們的閱讀不友好,我們看不懂,那是因?yàn)閞edisTemplate中默認(rèn)用了jdk自帶的序列化器

要想讓數(shù)據(jù)變成我們能看得懂的樣子,我們需要替換掉redisTempalte默認(rèn)的序列化器,現(xiàn)在我就來實(shí)操一下,寫一個(gè)配置類
@Configuration
public class RedisConfig {
//這里的上下文已經(jīng)有了自定義的redisTemplate,所以默認(rèn)的redisTemplate不會生效
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object,Object> redisTemplate=new RedisTemplate<>();
//設(shè)置自定義序列化器
redisTemplate.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
然后我改寫一下測試方法,一起來看結(jié)果
public String test(){
redisTemplate.opsForSet().add("ffffff","55555555");
return "OK";
}

我們看到,序列化器已經(jīng)生效了,鍵值對已經(jīng)是我們能看得懂的了。
總結(jié)
通過springboot整合redis的過程,我?guī)Т蠹曳治隽艘幌聅pringboot的自動(dòng)配置原理,基本上市面上流行的組件可以和spring整合的spring官方都有starter,引入starter,配合springboot的自動(dòng)配置,基本上可以做到只需要幾行屬性的配置加上類的注入,就可以使用了,spring框架博大精深,還有很多很多東西需要學(xué)習(xí),有時(shí)間我再給大家分享,望大家多多支持,謝謝。
以上就是springboot自動(dòng)配置原理解析的詳細(xì)內(nèi)容,更多關(guān)于springboot自動(dòng)配置原理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java POI實(shí)現(xiàn)將導(dǎo)入Excel文件的示例代碼
這篇文章主要介紹了Java POI實(shí)現(xiàn)將導(dǎo)入Excel文件的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02
Spring MVC傳遞接收參數(shù)方式小結(jié)
大家在開發(fā)中經(jīng)常會用到Spring MVC Controller來接收請求參數(shù),主要常用的接收方式就是通過實(shí)體對象以及形參等方式、有些用于GET請求,有些用于POST請求,有些用于兩者,下面介紹幾種常見的Spring MVC傳遞接收參數(shù)的方式2021-11-11
菜鳥學(xué)習(xí)java設(shè)計(jì)模式之單例模式
這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之單例模式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
使用java寫的矩陣乘法實(shí)例(Strassen算法)
這篇文章主要給大家介紹了關(guān)于如何使用java寫的矩陣乘法(Strassen算法)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
mybatis xml注釋sql的注意事項(xiàng)及說明
這篇文章主要介紹了mybatis xml注釋sql的注意事項(xiàng)及說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
JVM調(diào)優(yōu)總結(jié) -Xms -Xmx -Xmn -Xss的使用
這篇文章主要介紹了JVM調(diào)優(yōu)總結(jié) -Xms -Xmx -Xmn -Xss的使用方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-07-07
Spring Boot的FailureAnalyzer機(jī)制及如何解救應(yīng)用啟動(dòng)危機(jī)
本文探討了FailureAnalyzer工具,它不僅能幫助我們快速識別和處理代碼中的錯(cuò)誤,還能極大地提升我們的開發(fā)效率,通過詳細(xì)的實(shí)例分析,我們了解了FailureAnalyzer如何通過自定義邏輯應(yīng)對不同類型的異常,讓程序員能夠更好地定位問題并迅速找到解決方案,感興趣的朋友一起看看吧2025-01-01

