最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot自動配置原理解析

 更新時間:2021年04月22日 11:45:46   作者:是時候改個好點(diǎn)的名字  
這篇文章主要介紹了springboot自動配置原理解析,幫助大家更好的理解和學(xué)習(xí)使用springboot,感興趣的朋友可以了解下

前言

小伙伴們都知道,現(xiàn)在市面上最流行的web開發(fā)框架就是springboot了,在springboot開始流行之前,我們都用的是strust2或者是springmvc框架來開發(fā)web應(yīng)用,但是這兩個框架都有一個特點(diǎn)就是配置非常的繁瑣,要寫一大堆的配置文件,spring在支持了注解開發(fā)之后稍微有些改觀但有的時候還是會覺得比較麻煩,這個時候springboot就體現(xiàn)出了它的優(yōu)勢,springboot只需要一個properties或者yml文件就可以簡化springmvc中在xml中需要配置的一大堆的bean,這就是因?yàn)閟pringboot有自動配置,那么springboot自動配置的原理是什么呢,今天我們就來通過源碼分析一下springboot的自動配置原理

開始

我以springboot整合redis為例,來向大家分析springboot的自動配置原理

首先創(chuàng)建一個springboot工程用來測試,然后在pom文件中引入springboot-starter-redis的啟動器依賴

<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

然后,在啟動類中注入redisTemplate類,redisTemplate為spring官方提供的對redis底層開發(fā)包(例如jedis)進(jìn)行了深度封裝的組件,使用redisTemplate可以優(yōu)雅的操作redis。我在啟動類中寫了一個測試方法,向redis寫入一條數(shù)據(jù)

@RequestMapping("/redistest")
    public String test(){
        redisTemplate.opsForSet().add("aaaaa","123456");
        return "OK";
    }

運(yùn)行這個方法,打開redis客戶端可以看到值已經(jīng)寫入了

先拋開這里的鍵和值讓人看不懂的問題,大家是不是覺得springboot整合redis要比普通的springmvc整合redis簡單多了?我只配置了redis的連接地址,端口號,注入了redisTemplate,就能開始操作redis了,那么springboot底層到底做了些什么使得整合變得如此的簡單了呢。

首先我們來看,springboot啟動類上都有一個@SpringbootApplication注解,那么這個注解是起什么作用的呢,讓我們點(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這個注解是由一系列的注解組合而成,這其中最重要的是@EnableAutoConfiguration和@ComponentScan,@ComponentScan的意思就是組件掃描注解,這個注解會自動注入所有在主程序所在包下的組件。比@ComponentScan注解更重要的就是@EnableAutoConfiguration注解了,這個注解的含義就是開啟自動裝配,直接把bean裝配到ioc容器中,@EnableAutoConfiguration也是一個組合注解,我們點(diǎn)進(jìn)去看一下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

這個地方我們主要看@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)兩個注解,首先來看@AutoConfigurationPackage注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

這個注解主要是獲取我們注解所在包下的組件去進(jìn)行注冊,大家看到這個@Import注解,那么這個注解是什么含義呢,

@Import注解用來導(dǎo)入@Configuration注解的配置類、聲明@Bean注解的bean方法、導(dǎo)入ImportSelector的實(shí)現(xiàn)類或?qū)隝mportBeanDefinitionRegistrar的實(shí)現(xiàn)類,這里這個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) {
		//首先判斷這個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 {
			//一個新的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)這個注解,我們來看看AutoConfigurationImportSelector這個類,這個類是我們自動裝配的導(dǎo)入選擇器,首先看這個類的第一個方法,其實(shí)也就是這個類的核心方法

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		//加載元數(shù)據(jù)
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		//獲得自動裝配的實(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,我們來看下這個方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		//從工廠中獲取自動配置類
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		//這句斷言很重要,告訴了我們工廠是去哪里找自動配置類的,這里顯然META-INF/spring.factories是一個路徑
		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;
	}

那我們就找一下這個路徑,去哪里找呢,我們看到這個類的包是org.springframework.boot.autoconfigure;那我們就到這個包的位置去找這個spring.factories,果不其然,我們點(diǎn)開這個文件

我們看到文件中有一行注釋這Auto configure,表示這些都是自動配置相關(guān)的類,這里我們不得不說spring框架真的是強(qiáng)大,這里面居然有100多個自動配置類,我們找到redis有關(guān)的自動配置類

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\

這里我們需要的肯定是第一個自動配置類,我們點(diǎn)進(jìn)去看看

@Configuration
//條件注解,某個class位于類路徑上,才會實(shí)例化一個Bean,這個類是redis操作的類
@ConditionalOnClass(RedisOperations.class)
//使得@ConfigurationProperties 注解的類生效,這個類是配置redis屬性的類
@EnableConfigurationProperties(RedisProperties.class)
//導(dǎo)入一些配置
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	//僅僅在當(dāng)前上下文中不存在某個對象時,才會實(shí)例化一個Bean,這個就是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è)置到了這個類中

//前綴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í)操一下,寫一個配置類

@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的自動配置原理,基本上市面上流行的組件可以和spring整合的spring官方都有starter,引入starter,配合springboot的自動配置,基本上可以做到只需要幾行屬性的配置加上類的注入,就可以使用了,spring框架博大精深,還有很多很多東西需要學(xué)習(xí),有時間我再給大家分享,望大家多多支持,謝謝。

以上就是springboot自動配置原理解析的詳細(xì)內(nèi)容,更多關(guān)于springboot自動配置原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java排查死鎖示例

    java排查死鎖示例

    這篇文章主要介紹了java排查死鎖示例,通過java中簡單的死鎖示例引出四種排查死鎖的工具,詳細(xì)講解請看全文,希望對大家有所幫助
    2021-08-08
  • Java中的throws異常處理及使用細(xì)節(jié)

    Java中的throws異常處理及使用細(xì)節(jié)

    本文給大家介紹Java中的throws異常處理注意事項(xiàng)和使用細(xì)節(jié),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-10-10
  • 詳解java8中的Stream數(shù)據(jù)流

    詳解java8中的Stream數(shù)據(jù)流

    Stream使用一種類似用SQL語句從數(shù)據(jù)庫查詢數(shù)據(jù)的直觀方式來提供一種對Java集合運(yùn)算和表達(dá)的高階抽象。接下來通過本文給大家分享java8中的Stream數(shù)據(jù)流知識,感興趣的朋友一起看看吧
    2017-10-10
  • MyBatis-Plus通用枚舉自動關(guān)聯(lián)注入的實(shí)現(xiàn)

    MyBatis-Plus通用枚舉自動關(guān)聯(lián)注入的實(shí)現(xiàn)

    本文主要介紹了MyBatis-Plus通用枚舉自動關(guān)聯(lián)注入的實(shí)現(xiàn),解決了繁瑣的配置,讓 mybatis 優(yōu)雅的使用枚舉屬性,感興趣的可以一起來了解一下
    2021-06-06
  • Java由淺入深細(xì)數(shù)數(shù)組的操作上

    Java由淺入深細(xì)數(shù)數(shù)組的操作上

    數(shù)組對于每一門編程語言來說都是重要的數(shù)據(jù)結(jié)構(gòu)之一,當(dāng)然不同語言對數(shù)組的實(shí)現(xiàn)及處理也不盡相同。Java?語言中提供的數(shù)組是用來存儲固定大小的同類型元素
    2022-04-04
  • Nett中的心跳機(jī)制與斷線重連詳解

    Nett中的心跳機(jī)制與斷線重連詳解

    這篇文章主要介紹了Nett中的心跳機(jī)制與斷線重連詳解,我們以客戶端發(fā)送心跳為例,平時我們的心跳實(shí)現(xiàn)方式可能是搞個定時器,定時發(fā)送是吧,但是在Netty中卻不一樣,心跳被稱為空閑檢測,需要的朋友可以參考下
    2023-12-12
  • Java多線程Thread基礎(chǔ)學(xué)習(xí)

    Java多線程Thread基礎(chǔ)學(xué)習(xí)

    每一個正在執(zhí)行的程序都是一個進(jìn)程,資源只有一塊,所以在同一時間段會有多個程序同時執(zhí)行,但是在一個時間點(diǎn)上,只能由一個程序執(zhí)行,多線程是在一個進(jìn)程的基礎(chǔ)之上的進(jìn)一步劃分,需要的朋友可以參考下
    2023-04-04
  • Intellij IDEA導(dǎo)入JAVA項(xiàng)目并啟動(圖文教程)

    Intellij IDEA導(dǎo)入JAVA項(xiàng)目并啟動(圖文教程)

    這篇文章主要介紹了Intellij IDEA導(dǎo)入JAVA項(xiàng)目并啟動,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • java中使用DES加密解密實(shí)例

    java中使用DES加密解密實(shí)例

    這篇文章主要介紹了java中使用DES加密解密實(shí)例,需要的朋友可以參考一下
    2014-01-01
  • MyBatis-Plus的物理刪除和邏輯刪除(使用場景)

    MyBatis-Plus的物理刪除和邏輯刪除(使用場景)

    數(shù)據(jù)庫中的數(shù)據(jù)刪除會分為兩種:物理刪除 和 邏輯刪除,接下來通過本文給大家介紹MyBatis-Plus的物理刪除和邏輯刪除使用場景分析,感興趣的朋友一起看看吧
    2021-09-09

最新評論

隆德县| 湘乡市| 汝南县| 长丰县| 永嘉县| 周口市| 措勤县| 晴隆县| 上思县| 正宁县| 奇台县| 凤城市| 南召县| 舒城县| 营口市| 玛多县| 永靖县| 邵阳县| 翁源县| 托克托县| 鄄城县| 丹江口市| 黑龙江省| 同德县| 怀安县| 正镶白旗| 米泉市| 鹿邑县| 外汇| 沙雅县| 马关县| 双城市| 宁津县| 彭泽县| 西充县| 霍州市| 穆棱市| 淅川县| 富源县| 平塘县| 木里|