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

Springboot自動(dòng)加載配置的原理解析

 更新時(shí)間:2021年10月17日 10:04:11   作者:好煩吃不胖  
Springboot遵循“約定優(yōu)于配置”的原則,使用注解對(duì)一些常規(guī)的配置項(xiàng)做默認(rèn)配置,減少或不使用xml配置,讓你的項(xiàng)目快速運(yùn)行起來(lái),這篇文章主要給大家介紹了關(guān)于Springboot自動(dòng)加載配置原理的相關(guān)資料,需要的朋友可以參考下

1、springboot自動(dòng)配置的原理初探

以下注解都在springboot的自動(dòng)化配置包中:spring-boot-autoconfigure。讀者朋友可以跟著一下步驟走一遍,應(yīng)該對(duì)自動(dòng)配置就有一定的認(rèn)知了。

1.springboot程序的入口是在啟動(dòng)類,該類有個(gè)關(guān)鍵注解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) })
public @interface SpringBootApplication {
    //略……
}

2.打開(kāi)SpringBootApplication注解,上面有個(gè)關(guān)鍵注解EnableAutoConfiguration

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

3.EnableAutoConfiguration上有個(gè)@Import(AutoConfigurationImportSelector.class),注意AutoConfigurationImportSelector,

@Import作用創(chuàng)建一個(gè)AutoConfigurationImportSelector的bean對(duì)象,并且加入IoC容器

	//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
//此處只貼了關(guān)鍵方法
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;
	}

4.AutoConfigurationImportSelector類中的getCandidateConfigurations方法代碼如上,其調(diào)用了SpringFactoriesLoader的loadFactoryNames方法,來(lái)獲取

configurations,此configurations列表其實(shí)就是要被自動(dòng)花配置的類。SpringFactoriesLoader的兩個(gè)重要方法如下:

//org.springframework.core.io.support.SpringFactoriesLoader
//只貼了兩個(gè)關(guān)鍵方法
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

//此方法返回的是即將要被自動(dòng)化配置的類的全限定類名,是從META-INF/spring.factories配置的,配置文件中有個(gè)org.springframework.boot.autoconfigure.EnableAutoConfiguration 其后面可配置多個(gè)想被自動(dòng)花配置的類
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullab等le 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));//META-INF/spring.factories
			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);
		}
	}

5.舉例分析,我們?cè)趕pring.factories中可以看到org.springframework.boot.autoconfigure.EnableAutoConfiguration后有一個(gè)org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,說(shuō)明springboot希望redis能夠自動(dòng)化配置。接著我們打開(kāi)RedisAutoConfiguration源碼查看。此處我故意沒(méi)復(fù)制源碼,用的截圖,可以看到截圖直接有報(bào)錯(cuò),編譯錯(cuò)誤,錯(cuò)誤的原因是我們還沒(méi)添加spring-boot-starter-data-redis的依賴。**這里有個(gè)問(wèn)題,為什么明明代碼都報(bào)錯(cuò),Cannot resolve symbol xxx(未找到類),但是我們的項(xiàng)目依然可以啟動(dòng)?不信你建立一個(gè)簡(jiǎn)單的springboot項(xiàng)目,只添加web依賴,手動(dòng)打開(kāi)RedisAutoConfiguration,發(fā)現(xiàn)是報(bào)紅錯(cuò)的,但是你啟動(dòng)項(xiàng)目,發(fā)現(xiàn)沒(méi)任何問(wèn)題,why??**這個(gè)問(wèn)題后面再解答,先接著看自動(dòng)配置的問(wèn)題。

6.先把RedisAutoConfiguration源碼復(fù)制出來(lái)方便我寫注釋,上面用截圖主要是讓大家看到報(bào)錯(cuò)

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@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;
	}

}

看源碼可知RedisAutoConfiguration上有一個(gè)Configuration和ConditionalOnClass注解,先分析這兩個(gè)。首先Configuration注解,代表這是個(gè)Java config配置類,和spring配置bean的xml文件是一個(gè)作用,都是用來(lái)實(shí)例化bean的,**但是注意還有個(gè)@ConditionalOnClass(RedisOperations.class)注解,這個(gè)注解的作用是當(dāng)RedisOperations.class這個(gè)類被找到后才會(huì)生效,如果沒(méi)找到此類,那么整個(gè)RedisAutoConfiguration就不會(huì)生效。**所以當(dāng)我們引入了redis的依賴,springboot首先會(huì)通過(guò)RedisAutoConfiguration的方法redisTemplate給我們?cè)O(shè)置一個(gè)默認(rèn)的redis配置,當(dāng)然這個(gè)方法上也有個(gè)注解@ConditionalOnMissingBean(name = "redisTemplate"),就是當(dāng)我們沒(méi)有手動(dòng)配redisTemplate這個(gè)bean它才會(huì)調(diào)用這個(gè)默認(rèn)的方法,注入一個(gè)redisTemplate到IoC容器,所以一般情況我們都是手動(dòng)配置這個(gè)redisTemplate,方便我們?cè)O(shè)置序列化器,如下:

@Configuration
public class RedisConfig {

    /**
     * 設(shè)置 redisTemplate 的序列化設(shè)置
     *
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 1.創(chuàng)建 redisTemplate 模版
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 2.關(guān)聯(lián) redisConnectionFactory
        template.setConnectionFactory(redisConnectionFactory);
        // 3.創(chuàng)建 序列化類
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 4.設(shè)置可見(jiàn)度
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 5.啟動(dòng)默認(rèn)的類型
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        // 6.序列化類,對(duì)象映射設(shè)置
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 7.設(shè)置 value 的轉(zhuǎn)化格式和 key 的轉(zhuǎn)化格式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

RedisAutoConfiguration上還有一下兩個(gè)注解,作用是從配置文件讀取redis相關(guān)的信息,ip、端口、密碼等

@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })

2. 補(bǔ)充擴(kuò)展(解釋為什么引用的包都報(bào)紅錯(cuò)了,項(xiàng)目還能啟動(dòng))

所有的@Condition注解(包括衍生的)其實(shí)都對(duì)應(yīng)一個(gè)具體的實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)類里面有個(gè)判斷方法叫做matches,返回的是個(gè)布爾類型判斷值。

打開(kāi)ConditionalOnClass源碼如下,其Conditional注解傳遞的是個(gè)OnClassCondition.class,這就其對(duì)應(yīng)的判斷類,也就是說(shuō),當(dāng)我們使用ConditionalOnClass注解時(shí),其實(shí)際上調(diào)用的是OnClassCondition來(lái)判斷的

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

	/**
	 * The classes that must be present. Since this annotation is parsed by loading class
	 * bytecode, it is safe to specify classes here that may ultimately not be on the
	 * classpath, only if this annotation is directly on the affected component and
	 * <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
	 * use this annotation as a meta-annotation, only use the {@link #name} attribute.
	 * @return the classes that must be present
	 */
	Class<?>[] value() default {};

	/**
	 * The classes names that must be present.
	 * @return the class names that must be present.
	 */
	String[] name() default {};

}

ConditionalOnClass類圖如下,它繼承了condition接口

打開(kāi)Condition接口如下,查看注釋,注釋中有說(shuō)明 **條件判斷是在bean定義即將注冊(cè)到容器之前進(jìn)行的,**看過(guò)springIoC源碼的同學(xué)應(yīng)該知道,spring創(chuàng)建一個(gè)對(duì)象的過(guò)程是當(dāng)服務(wù)啟動(dòng)后,先讀取xml配置文件(或者通過(guò)注解),根據(jù)配置文件先定義一個(gè)BeanDefinition,然后把這個(gè)bean給放到容器(在spring中實(shí)際就是一個(gè)Map),然后在根據(jù)bean定義,通過(guò)反射創(chuàng)建真正的對(duì)象。反射會(huì)觸發(fā)類加載,當(dāng)condition條件不滿足時(shí),根據(jù)如下注釋可知,bean定義后續(xù)都被攔截了,連注冊(cè)都不行,所以自然就不可能通過(guò)反射創(chuàng)建對(duì)象,不反射自然不會(huì)觸發(fā)類加載,不觸發(fā)類加載那么RedisAutoConfiguration當(dāng)然啊不會(huì)加載,它不加載,那么即使它里面引用了一個(gè)不存在的類也不會(huì)有啥問(wèn)題。

上面說(shuō)的很繞,表達(dá)的不是很好,要想看懂以上部分需要掌握兩方面的知識(shí):

  • 類加載原理,推薦看周志明老師的《深入理解JVM虛擬機(jī)》
  • spring IoC容器創(chuàng)建bean的原理,推薦《spring揭秘》,詳細(xì)看看IoC部分

3、又一個(gè)問(wèn)題

spring-boot-autoconfigure.jar這個(gè)包中的RedisAutoConfiguration都報(bào)紅色錯(cuò)誤了,那么spring官方是怎么打包出來(lái)spring-boot-autoconfigure.jar的??怎么給我們提供了一個(gè)報(bào)錯(cuò)的包呢

//TODO

總結(jié)

到此這篇關(guān)于Springboot自動(dòng)加載配置原理的文章就介紹到這了,更多相關(guān)Springboot自動(dòng)加載配置原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot 實(shí)現(xiàn)mqtt物聯(lián)網(wǎng)的示例代碼

    springboot 實(shí)現(xiàn)mqtt物聯(lián)網(wǎng)的示例代碼

    這篇文章主要介紹了springboot 實(shí)現(xiàn)mqtt物聯(lián)網(wǎng),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Java中16條的代碼規(guī)范

    Java中16條的代碼規(guī)范

    如何更規(guī)范化編寫Java 代碼的重要性想必毋需多言,其中最重要的幾點(diǎn)當(dāng)屬提高代碼性能、使代碼遠(yuǎn)離Bug、令代碼更優(yōu)雅,
    2021-07-07
  • Java日志框架之logback使用詳解

    Java日志框架之logback使用詳解

    這篇文章主要介紹了Java日志框架之logback使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java通過(guò)動(dòng)態(tài)規(guī)劃設(shè)計(jì)股票買賣最佳時(shí)機(jī)

    Java通過(guò)動(dòng)態(tài)規(guī)劃設(shè)計(jì)股票買賣最佳時(shí)機(jī)

    動(dòng)態(tài)規(guī)劃可謂是大名鼎鼎,筆試面試中的高頻考點(diǎn),也是重點(diǎn)難點(diǎn),動(dòng)態(tài)規(guī)劃類型題目靈活多變,難度系數(shù)也相對(duì)較高,往往我們做不好動(dòng)態(tài)規(guī)劃的題目就會(huì)與心儀的offer失之交臂,本篇文章我們就一起來(lái)研究一下動(dòng)態(tài)規(guī)劃設(shè)計(jì)股票買賣最佳時(shí)機(jī)
    2022-10-10
  • maven的5種打包方式小結(jié)

    maven的5種打包方式小結(jié)

    本文主要介紹了maven的5種打包方式小結(jié),主要是幾種插件打包,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • Java設(shè)計(jì)模式之策略模式示例詳解

    Java設(shè)計(jì)模式之策略模式示例詳解

    這篇文章主要為大家詳細(xì)介紹了Java的策略模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • Java反射的定義和用法詳解

    Java反射的定義和用法詳解

    Java中的反射是指在程序運(yùn)行時(shí)動(dòng)態(tài)地獲取和操作類、方法、屬性等元素的能力。它使得我們可以在程序運(yùn)行時(shí)獲取一個(gè)類的信息,并對(duì)其進(jìn)行操作,需要的朋友可以參考下
    2023-05-05
  • Java?多線程并發(fā)?ReentrantReadWriteLock詳情

    Java?多線程并發(fā)?ReentrantReadWriteLock詳情

    這篇文章主要介紹了Java多線程并發(fā)ReentrantReadWriteLock詳情,ReentrantReadWriteLock可重入讀寫鎖。實(shí)際使用場(chǎng)景中,我們需要處理的操作本質(zhì)上是讀與寫,更多相關(guān)資料,感興趣的小伙伴可以參考一下下面文章內(nèi)容
    2022-06-06
  • 一文帶你解讀Java序列化和反序列化機(jī)制

    一文帶你解讀Java序列化和反序列化機(jī)制

    序列化是將對(duì)象轉(zhuǎn)換成二進(jìn)制字節(jié)流的過(guò)程;反序列化是從二進(jìn)制字節(jié)流中恢復(fù)對(duì)象的過(guò)程。本文將大家詳細(xì)講講二者的原理與實(shí)現(xiàn),希望對(duì)大家有所幫助
    2023-01-01
  • FeignClient設(shè)置動(dòng)態(tài)url方式

    FeignClient設(shè)置動(dòng)態(tài)url方式

    文章介紹了如何在Spring Cloud環(huán)境下使用FeignClient實(shí)現(xiàn)負(fù)載均衡,通過(guò)配置Nacos和FeignClient屬性,可以實(shí)現(xiàn)服務(wù)間的負(fù)載均衡調(diào)用
    2024-11-11

最新評(píng)論

祁阳县| 子长县| 泰顺县| 长春市| 海南省| 彝良县| 淮北市| 崇左市| 五大连池市| 赤峰市| 玛曲县| 临泽县| 鱼台县| 商洛市| 嘉义县| 色达县| 宣汉县| 鱼台县| 泰宁县| 蛟河市| 杭锦旗| 迁西县| 密山市| 永城市| 常宁市| 鹿泉市| 额济纳旗| 上蔡县| 布尔津县| 灵山县| 西乡县| 武强县| 武安市| 呼图壁县| 乌鲁木齐市| 枣强县| 托克逊县| 宁阳县| 石渠县| 安溪县| 临潭县|