淺談springboot自動(dòng)配置原理
從main函數(shù)說起
一切的開始要從SpringbootApplication注解說起。
@SpringBootApplication public class MyBootApplication { public static void main(String[] args) { SpringApplication.run(MyBootApplication.class); } } @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan public @interface SpringBootApplication { }
其中最重要的就是EnableAutoConfiguration注解,開啟自動(dòng)配置。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
通過Import注解導(dǎo)入AutoConfigurationImportSelector。在這個(gè)類中加載/META-INF/spring.factories文件的信息,然后篩選出以EnableAutoConfiguration為key的數(shù)據(jù),加載到IOC容器中,實(shí)現(xiàn)自動(dòng)配置功能。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
從表面看就是自動(dòng)配置包,主要使用了Import注解,導(dǎo)入了Registrar類。這里Registrar類的registerBeanDefinitions方法導(dǎo)包,也就是導(dǎo)入當(dāng)前main函數(shù)所在路徑的包地址,我這里是com.zhangfei。
怎么自動(dòng)裝配其他N個(gè)類
Import({AutoConfigurationImportSelector.class})該注解給當(dāng)前配置類導(dǎo)入另外N個(gè)自動(dòng)配置類。
這里既然導(dǎo)入N個(gè)自動(dòng)配置類,那么都導(dǎo)入哪些類呢?
//AutoConfigurationImportSelector實(shí)現(xiàn)DeferredImportSelector接口,而DeferredImportSelector接口又繼承了ImportSelector public interface ImportSelector { String[] selectImports(AnnotationMetadata var1); }
AutoConfigurationImportSelector通過實(shí)現(xiàn)接口ImportSelector的selectImports方法返回需要導(dǎo)入的組件,selectImports方法返回一個(gè)全類名字符串?dāng)?shù)組。
主角上場(chǎng)
//AutoConfigurationImportSelector.java @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); 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); 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); } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()); return configurations; }
這里又開始調(diào)用SpringFactoriesLoader.loadFactoryNames。
SpringFactoriesLoader.loadFactoryNames方法中關(guān)鍵的三步:
(1)從當(dāng)前項(xiàng)目的類路徑中獲取所有 META-INF/spring.factories 這個(gè)文件下的信息.
(2)將上面獲取到的信息封裝成一個(gè) Map 返回,EnableAutoConfiguration為key。
(3)從返回的Map中通過剛才傳入的 EnableAutoConfiguration.class參數(shù),獲取該 key 下的所有值。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
自動(dòng)配置都有哪些內(nèi)容呢?
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ ...其他省略
XXXAutoConfiguration和XXProperties
在spring.factories文件中看到的都是自動(dòng)配置類,那么自動(dòng)配置用到的屬性值在那里呢?我們拿出redis為例
@Configuration @ConditionalOnClass(RedisOperations.class) //判斷當(dāng)前項(xiàng)目有沒有這個(gè)類RedisOperations.class @EnableConfigurationProperties(RedisProperties.class) //啟用配置屬性,這里看到了熟悉的XXXProperties @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) //導(dǎo)入這兩個(gè)類 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; } } //這里則保存redis初始化時(shí)的屬性 @ConfigurationProperties(prefix = "spring.redis") public class RedisProperties { private int database = 0; private String url; private String host = "localhost"; private String password; private int port = 6379; private boolean ssl; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java SpringBoot自動(dòng)配置原理詳情
- Springboot自動(dòng)配置與@Configuration配置類詳解
- SpringBoot自動(dòng)配置實(shí)現(xiàn)的詳細(xì)步驟
- SpringBoot自動(dòng)配置原理詳解
- SpringBoot自動(dòng)配置Quartz的實(shí)現(xiàn)步驟
- springboot自動(dòng)配置原理以及spring.factories文件的作用詳解
- springboot自動(dòng)配置原理解析
- SpringBoot自動(dòng)配置的實(shí)現(xiàn)原理
- Springboot的自動(dòng)配置是什么及注意事項(xiàng)
相關(guān)文章
淺談利用Spring的AbstractRoutingDataSource解決多數(shù)據(jù)源的問題
本篇文章主要介紹了淺談利用Spring的AbstractRoutingDataSource解決多數(shù)據(jù)源的問題,具有一定的參考價(jià)值,有需要的可以了解一下2017-08-08Spring定時(shí)任務(wù)無故停止又不報(bào)錯(cuò)的解決
這篇文章主要介紹了Spring定時(shí)任務(wù)無故停止又不報(bào)錯(cuò)的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11基于Java多線程notify與notifyall的區(qū)別分析
本篇文章對(duì)Java中多線程notify與notifyall的區(qū)別進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05在SpringBoot中配置MySQL數(shù)據(jù)庫的詳細(xì)指南
在 Spring Boot 中配置數(shù)據(jù)庫是一個(gè)相對(duì)簡(jiǎn)單的過程,通常涉及到以下幾個(gè)步驟:添加數(shù)據(jù)庫驅(qū)動(dòng)依賴、配置數(shù)據(jù)源屬性、以及可選的配置 JPA(如果使用),下面是小編給大家編寫的一個(gè)詳細(xì)的指南,以MySQL 數(shù)據(jù)庫為例,需要的朋友可以參考下2024-12-12java中實(shí)體類實(shí)現(xiàn)時(shí)間日期自動(dòng)轉(zhuǎn)換方式
這篇文章主要介紹了java中實(shí)體類實(shí)現(xiàn)時(shí)間日期自動(dòng)轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06springboot實(shí)現(xiàn)異步任務(wù)
這篇文章主要為大家詳細(xì)介紹了springboot實(shí)現(xiàn)異步任務(wù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05Java?Servlet實(shí)現(xiàn)表白墻的代碼實(shí)例
最近用Servlet做了個(gè)小項(xiàng)目,分享給大家,下面這篇文章主要給大家介紹了關(guān)于Java?Servlet實(shí)現(xiàn)表白墻的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02