摘要:所以,所謂的自動裝配,實際上就是如何自動將裝載到容器中來。實際上在版本中,模塊驅動注解的出現,已經有了一定的自動裝配的雛形,而真正能夠實現這一機制,還是在版本中,條件注解的出現。,我們來看一下的自動裝配是怎么一回事。
在前面的分析中,Spring Framework一直在致力于解決一個問題,就是如何讓bean的管理變得更簡單,如何讓開發者盡可能的少關注一些基礎化的bean的配置,從而實現自動裝配。所以,所謂的自動裝配,實際上就是如何自動將bean裝載到Ioc容器中來。
實際上在spring 3.x版本中,Enable模塊驅動注解的出現,已經有了一定的自動裝配的雛形,而真正能夠實現這一機制,還是在spirng 4.x版本中,conditional條件注解的出現。ok,我們來看一下spring boot的自動裝配是怎么一回事。
org.springframework.boot spring-boot-starter-data-redis
spring: redis: host: 127.0.0.1 port: 6379
@Autowired private RedisTemplateredisTemplate;
按照下面的順序添加starter,然后添加配置,使用RedisTemplate就可以使用了? 那大家想沒想過一個問題,為什么RedisTemplate可以被直接注入?它是什么時候加入到Ioc容器的呢? 這就是自動裝配。自動裝配可以使得classpath下依賴的包相關的bean,被自動裝載到Spring Ioc容器中,怎么做到的呢?
EnableAutoConfiguration的主要作用其實就是幫助springboot應用把所有符合條件的@Configuration配置都加載到當前SpringBoot創建并使用的IoC容器中。
再回到EnableAutoConfiguration這個注解中,我們發現它的import是這樣
@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {
但是從EnableAutoCOnfiguration上面的import注解來看,這里面并不是引入另外一個Configuration。而是一個ImportSelector。這個是什么東西呢?
Enable注解不僅僅可以像前面演示的案例一樣很簡單的實現多個Configuration的整合,還可以實現一些復雜的場景,比如可以根據上下文來激活不同類型的bean,@Import注解可以配置三種不同的class
實現ImportBeanDefinitionRegistrar接口進行動態注入
public class CacheService {
}
public class LoggerService {
}
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented @Inherited --允許被繼承@Import({GpDefineImportSelector.class})public @interface EnableDefineService { String[] packages() default "";}
public class GpDefineImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { //獲得指定注解的詳細信息。我們可以根據注解中配置的屬性來返回不同的class, //從而可以達到動態開啟不同功能的目的 annotationMetadata.getAllAnnotationAttributes(EnableDefineService.class.getName(),true) .forEach((k,v) -> { log.info(annotationMetadata.getClassName()); log.info("k:{},v:{}",k,String.valueOf(v)); }); return new String[]{CacheService.class.getName()}; }}
@SpringBootApplication@EnableDefineService(name = "gupao",value = "gupao")public class EnableDemoTest { public static void main(String[] args) { ConfigurableApplicationContext ca=SpringApplication.run(EnableDemoTest.class,args); System.out.println(ca.getBean(CacheService.class)); System.out.println(ca.getBean(LoggerService.class)); }}
了解了selector的基本原理之后,后續再去分析AutoConfigurationImportSelector的原理就很簡單了,它本質上也是對于bean的動態加載。
了解了ImportSelector和ImportBeanDefinitionRegistrar后,對于EnableAutoConfiguration的理解就容易一些了
它會通過import導入第三方提供的bean的配置類:AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class)
從名字來看,可以猜到它是基于ImportSelector來實現基于動態bean的加載功能。之前我們講過Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的數組(類的全類名)都會被納入到spring容器中。
那么可以猜想到這里的實現原理也一定是一樣的,定位到AutoConfigurationImportSelector這個類中的selectImports方法
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }// 從配置文件(spring-autoconfigure-metadata.properties)中加載 AutoConfigurationMetadata AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);// 獲取所有候選配置類EnableAutoConfiguration 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);//使用SpringFactoriesLoader 加載classpath路徑下META-INF/spring.factories中,//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration對應的value List configurations = getCandidateConfigurations(annotationMetadata, attributes);//去重 configurations = removeDuplicates(configurations);//應用exclusion屬性 Set exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);//過濾,檢查候選配置類上的注解@ConditionalOnClass,如果要求的類不存在,則這個候選類會被過濾不被加載 configurations = filter(configurations, autoConfigurationMetadata); //廣播事件fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions);}
本質上來說,其實EnableAutoConfiguration會幫助springboot應用把所有符合@Configuration配置都加載到當前SpringBoot創建的IoC容器,而這里面借助了Spring框架提供的一個工具類SpringFactoriesLoader的支持。以及用到了Spring提供的條件注解@Conditional,選擇性的針對需要加載的bean進行條件過濾
為了給大家補一下基礎,我在這里簡單分析一下SpringFactoriesLoader這個工具類的使用。它其實和java中的SPI機制的原理是一樣的,不過它比SPI更好的點在于不會一次性加載所有的類,而是根據key進行加載。
首先,SpringFactoriesLoader的作用是從classpath/META-INF/spring.factories文件中,根據key來加載對應的類到spring IoC容器中。接下來帶大家實踐一下
org.springframework spring-context 4.3.13.RELEASE
public class GuPaoCore { public String study(){ System.out.println("good good study, day day up"); return "GuPaoEdu.com"; }}@Configurationpublic class GuPaoConfig { @Bean public GuPaoCore guPaoCore(){ return new GuPaoCore(); }}
把前面的工程打包成jar,當前項目依賴該jar包
com.gupaoedu.practice Gupao-Core 1.0-SNAPSHOT
運行結果會報錯,原因是GuPaoCore并沒有被Spring的IoC容器所加載,也就是沒有被EnableAutoConfiguration導入
@SpringBootApplicationpublic class SpringBootStudyApplication { public static void main(String[] args) throws IOException { ConfigurableApplicationContext ac=SpringApplication.run(SpringBootStudyApplication.class, args); GuPaoCore gpc=ac.getBean(GuPaoCore.class); System.out.println(gpc.study()); }}
在GuPao-Core項目resources下新建文件夾META-INF,在文件夾下面新建spring.factories文件,文件中配置,key為自定配置類EnableAutoConfiguration的全路徑,value是配置類的全路徑
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gupaoedu.practice.GuPaoConfig
重新打包,重新運行SpringBootStudyApplication這個類。
可以發現,我們編寫的那個類,就被加載進來了。
了解了ImportSelector和ImportBeanDefinitionRegistrar后,對于EnableAutoConfiguration的理解就容易一些了
它會通過import導入第三方提供的bean的配置類:AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class)
從名字來看,可以猜到它是基于ImportSelector來實現基于動態bean的加載功能。之前我們講過Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的數組(類的全類名)都會被納入到spring容器中。
那么可以猜想到這里的實現原理也一定是一樣的,定位到AutoConfigurationImportSelector這個類中的selectImports方法
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }// 從配置文件(spring-autoconfigure-metadata.properties)中加載 AutoConfigurationMetadata AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);// 獲取所有候選配置類EnableAutoConfiguration 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);//使用SpringFactoriesLoader 加載classpath路徑下META-INF/spring.factories中,//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration對應的value List configurations = getCandidateConfigurations(annotationMetadata, attributes);//去重 configurations = removeDuplicates(configurations);//應用exclusion屬性 Set exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);//過濾,檢查候選配置類上的注解@ConditionalOnClass,如果要求的類不存在,則這個候選類會被過濾不被加載 configurations = filter(configurations, autoConfigurationMetadata); //廣播事件fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions);}
本質上來說,其實EnableAutoConfiguration會幫助springboot應用把所有符合@Configuration配置都加載到當前SpringBoot創建的IoC容器,而這里面借助了Spring框架提供的一個工具類SpringFactoriesLoader的支持。以及用到了Spring提供的條件注解@Conditional,選擇性的針對需要加載的bean進行條件過濾
版權聲明:本博客所有文章除特別聲明外,均采用 CC BY-NC-SA 4.0 許可協議。轉載請注明來自
Mic帶你學架構
!
如果本篇文章對您有幫助,還請幫忙點個關注和贊,您的堅持是我不斷創作的動力。歡迎關注「跟著Mic學架構」公眾號公眾號獲取更多技術干貨!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/125380.html
摘要:引入了新的環境和概要信息,是一種更揭秘與實戰六消息隊列篇掘金本文,講解如何集成,實現消息隊列。博客地址揭秘與實戰二數據緩存篇掘金本文,講解如何集成,實現緩存。 Spring Boot 揭秘與實戰(九) 應用監控篇 - HTTP 健康監控 - 掘金Health 信息是從 ApplicationContext 中所有的 HealthIndicator 的 Bean 中收集的, Spring...
摘要:是的簡稱,運行環境,為的運行提供了所需的環境。分割字符串,返回分割后的字符串數組。當計算的值相同時,我們稱之為沖突,的做法是用鏈表和紅黑樹存儲相同的值的。迭代器取代了集合框架中的,迭代器允許調用者在迭代過程中移除元素。 Java基礎1.JDK和JRE有什么區別? JDK 是java development kit的簡稱,java開發工具包,提供java的開發環境和運行環境。JRE 是j...
摘要:的簡稱,運行環境,為的運行提供了所需環境。分割字符串,返回一個分割后的字符串數組。線程安全是線程安全的,而是非線程安全的。迭代器取代了集合框架中的,迭代器允許調用者在迭代過程中移除元素。 本文分為十九個模塊,分別是:?Java 基礎、容器、多線程、反射、對象拷貝、Java Web 、異常、網絡、設計模式、Spring/Spring MVC、Spring Boot/Spring Clou...
摘要:你可以試著沿著調用棧代碼一層一層的深入進去,如果你不打斷點,你根本不知道接下來程序會往哪里流動。接下來再看看運行時堆棧,看看一個請求的調用棧有多深。就是如此被自動裝配進的。 摘要: 神奇的SpringBoot。 原文:SpringBoot 究竟是如何跑起來的? 作者:老錢 Fundebug經授權轉載,版權歸原作者所有。 不得不說 SpringBoot 太復雜了,我本來只想研究一下...
閱讀 3735·2023-01-11 11:02
閱讀 4244·2023-01-11 11:02
閱讀 3050·2023-01-11 11:02
閱讀 5180·2023-01-11 11:02
閱讀 4737·2023-01-11 11:02
閱讀 5534·2023-01-11 11:02
閱讀 5313·2023-01-11 11:02
閱讀 3990·2023-01-11 11:02