一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Springboot自動加載配置的原理解析

Springboot自動加載配置的原理解析

2022-02-21 13:15好煩吃不胖 Java教程

Springboot遵循“約定優(yōu)于配置”的原則,使用注解對一些常規(guī)的配置項做默認配置,減少或不使用xml配置,讓你的項目快速運行起來,這篇文章主要給大家介紹了關于Springboot自動加載配置原理的相關資料,需要的朋友可以參考下

 

1、springboot自動配置的原理初探

以下注解都在springboot的自動化配置包中:spring-boot-autoconfigure。讀者朋友可以跟著一下步驟走一遍,應該對自動配置就有一定的認知了。

1.springboot程序的入口是在啟動類,該類有個關鍵注解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.打開SpringBootApplication注解,上面有個關鍵注解EnableAutoConfiguration

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

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

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

	//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
//此處只貼了關鍵方法
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方法代碼如上,其調用了SpringFactoriesLoader的loadFactoryNames方法,來獲取

configurations,此configurations列表其實就是要被自動花配置的類。SpringFactoriesLoader的兩個重要方法如下:

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

//此方法返回的是即將要被自動化配置的類的全限定類名,是從META-INF/spring.factories配置的,配置文件中有個org.springframework.boot.autoconfigure.EnableAutoConfiguration 其后面可配置多個想被自動花配置的類
    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.舉例分析,我們在spring.factories中可以看到org.springframework.boot.autoconfigure.EnableAutoConfiguration后有一個org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,說明springboot希望redis能夠自動化配置。接著我們打開RedisAutoConfiguration源碼查看。此處我故意沒復制源碼,用的截圖,可以看到截圖直接有報錯,編譯錯誤,錯誤的原因是我們還沒添加spring-boot-starter-data-redis的依賴。**這里有個問題,為什么明明代碼都報錯,Cannot resolve symbol xxx(未找到類),但是我們的項目依然可以啟動?不信你建立一個簡單的springboot項目,只添加web依賴,手動打開RedisAutoConfiguration,發(fā)現(xiàn)是報紅錯的,但是你啟動項目,發(fā)現(xiàn)沒任何問題,why??**這個問題后面再解答,先接著看自動配置的問題。

Springboot自動加載配置的原理解析

6.先把RedisAutoConfiguration源碼復制出來方便我寫注釋,上面用截圖主要是讓大家看到報錯

@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上有一個Configuration和ConditionalOnClass注解,先分析這兩個。首先Configuration注解,代表這是個Java config配置類,和spring配置bean的xml文件是一個作用,都是用來實例化bean的,**但是注意還有個@ConditionalOnClass(RedisOperations.class)注解,這個注解的作用是當RedisOperations.class這個類被找到后才會生效,如果沒找到此類,那么整個RedisAutoConfiguration就不會生效。**所以當我們引入了redis的依賴,springboot首先會通過RedisAutoConfiguration的方法redisTemplate給我們設置一個默認的redis配置,當然這個方法上也有個注解@ConditionalOnMissingBean(name = "redisTemplate"),就是當我們沒有手動配redisTemplate這個bean它才會調用這個默認的方法,注入一個redisTemplate到IoC容器,所以一般情況我們都是手動配置這個redisTemplate,方便我們設置序列化器,如下:

@Configuration
public class RedisConfig {

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

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

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

 

2. 補充擴展(解釋為什么引用的包都報紅錯了,項目還能啟動)

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

打開ConditionalOnClass源碼如下,其Conditional注解傳遞的是個OnClassCondition.class,這就其對應的判斷類,也就是說,當我們使用ConditionalOnClass注解時,其實際上調用的是OnClassCondition來判斷的

@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接口

Springboot自動加載配置的原理解析

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

Springboot自動加載配置的原理解析

上面說的很繞,表達的不是很好,要想看懂以上部分需要掌握兩方面的知識:

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

 

3、又一個問題

spring-boot-autoconfigure.jar這個包中的RedisAutoConfiguration都報紅色錯誤了,那么spring官方是怎么打包出來spring-boot-autoconfigure.jar的??怎么給我們提供了一個報錯的包呢

//TODO

 

總結

到此這篇關于Springboot自動加載配置原理的文章就介紹到這了,更多相關Springboot自動加載配置原理內容請搜索服務器之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://blog.csdn.net/chen462488588/article/details/120769282

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美男同互吃gay老头 | 99精品久久精品一区二区小说 | 免费网站视频 | 免费全看男女拍拍拍的视频 | 91混血大战上海双胞胎 | 国产在线观看网站 | 91亚洲在线| 日日碰碰 | 动漫美女3d被爆漫画 | 亚洲欧美国产另类 | 99色亚洲 | 夫妇交换小说 | 操闺蜜 | 色哟哟久久 | 茄子视频懂你更多apl | 国产福利在线观看第二区 | 国产成人精品一区二三区在线观看 | jazz中国在线视频 | 国产美女做爰免费视频软件 | 日本高清中文字幕视频在线 | 欧美高清无砖专区欧美精品 | 无人区乱码1区2区3区网站 | 男人女人日皮 | 热久久99精品这里有精品 | 成人人免费夜夜视频观看 | 欧美视频在线一区二区三区 | 欧美成人免费观看久久 | 亚洲精品中文字幕在线 | 色婷婷精品 | 91在线老王精品免费播放 | 欧美专区在线播放 | 明星h文集合短篇小说 | 男人的天堂久久精品激情 | 久九九精品免费视频 | 色综合综合 | 91av爱爱| 免费黄色片在线观看 | 青青色综合 | 亚洲成在人线视频 | 跪在老师脚下吃丝袜脚 | 91亚洲精品第一综合不卡播放 |