首先是@SpringBootApplication(自動化裝配功能)
點進去源碼發現
先來看看每個注解的意思
可以發現它是由眾多注解組合而成的,下面具體分析下這里每個注解所起到的作用。
- @Target Target通過ElementType來指定注解可使用范圍的枚舉集合(FIELD/METHOD/PARAMETER...)
-
@Retention Retention(保留)注解說明,這種類型的注解會被保留到那個階段. 有三個值:
- RetentionPolicy.SOURCE —— 這種類型的Annotations只在源代碼級別保留,編譯時就會被忽略
- RetentionPolicy.CLASS —— 這種類型的Annotations編譯時被保留,在class文件中存在,但JVM將會忽略
- RetentionPolicy.RUNTIME —— 這種類型的Annotations將被JVM保留,所以他們能在運行時被JVM或其他使
- @Documented 注解表明這個注解應該被 javadoc工具記錄. 默認情況下,javadoc是不包括注解的. 但如果聲明注解時指定了 @Documented,則它會被 javadoc 之類的工具處理, 所以注解類型信息也會被包括在生成的文檔中
- @Inherited 允許子類繼承父類的注解,僅限於類注解有用,對於方法和屬性無效。
- @SpringBootConfiguration 注解實際上和@Configuration有相同的作用,配備了該注解的類就能夠以JavaConfig的方式完成一些配置,可以不再使用XML配置。
- @ComponentScan 這個注解完成的是自動掃描的功能,相當於Spring XML配置文件中的:
<context:component-scan>
,可使用basePackages屬性指定要掃描的包,及掃描的條件。如果不設置則默認掃描@ComponentScan注解所在類的同級類和同級目錄下的所有類,所以我們的Spring Boot項目,一般會把入口類放在頂層目錄中,這樣就能夠保證源碼目錄下的所有類都能夠被掃描到。 -
@EnableAutoConfiguration 這個注解是讓Spring Boot的配置能夠如此簡化的關鍵性注解。
其實主要重要的注解就是標紅的這三個注解:下面我們着重分析這三個注解。
1)@ComponentScan:就像上面所說,ComponentScan一般在spring中主要是用於:
@ComponentScan用於類或接口上主要是指定掃描路徑,spring會把指定路徑下帶有指定注解的類自動裝配到bean容器里,會被自動裝配的注解包括@Controller、@Service、@Component、@Repository等等,其作用等同於<context:component-scan base-package="com.maple.learn" />配置。
而在springboot中,如果你的其他包都在使用了@SpringBootApplication注解的main app所在的包及其下級包,則你什么都不用做,SpringBoot會自動幫你把其他包都掃描了,如果你有一些bean所在的包,不在main app的包及其下級包,那么你需要手動加上@ComponentScan注解並指定那個bean所在的包。(后面在解釋在這里的原理)
2)@SpringBootConfiguration :先來一張其源碼截圖
這里說明SpringBootConfiguration 其實是對spring的@Configuration的封裝,相當於一個配置類,注解主要標注在某個類上,相當於xml配置文件中的<beans>。並會將當前類內聲明的一個或多個以@Bean注解的方法的實例納入到spring容器中,並且實例名就是方法名。本身其實也是一個IoC容器的配置類。
3)@EnableAutoConfiguration 這個才是重點,點擊源碼發現
先來聊聊這個注解:@AutoConfigurationPackage
AutoConfigurationPackage注解的作用是將 添加該注解的類所在的package 作為 自動配置package 進行管理。
AutoConfigurationPackage注解:
1 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { 2 3 @Override 4 public void registerBeanDefinitions(AnnotationMetadata metadata, 5 BeanDefinitionRegistry registry) { 6 register(registry, new PackageImport(metadata).getPackageName()); 7 }
它其實是注冊了一個Bean的定義。
new PackageImport(metadata).getPackageName(),它其實返回了當前主程序類的 同級以及子級 的包組件。
以上圖為例,DemoApplication是和demo包同級,但是demo2這個類是DemoApplication的父級,和example包同級
也就是說,DemoApplication啟動加載的Bean中,並不會加載demo2,這也就是為什么,我們要把DemoApplication放在項目的最高級中。
這里來談談其與componentScan的區別:
在默認的情況下就是將:主配置類(@SpringBootApplication
)的所在包及其子包里邊的組件掃描到Spring容器中。
- 看完這句話,會不會覺得,這不就是ComponentScan的功能嗎?這倆不就重復了嗎?
看看文檔的這句話:
it will be used when scanning for code @Entity classes.
It is generally recommended that you place EnableAutoConfiguration (if you’re
not using @SpringBootApplication) in a root package so that all sub-packages
and classes can be searched.
比如說,你用了Spring Data JPA,可能會在實體類上寫@Entity
注解。這個@Entity
注解由@AutoConfigurationPackage
掃描並加載,而我們平時開發用的@Controller/@Service/@Component/@Repository
這些注解是由ComponentScan
來掃描並加載的。
簡單理解:這二者掃描的對象是不一樣的。
4)下面來看看一個重中之重的注解:@EnableAutoConfiguration
老規矩進行這個重要注解之前,要先鋪墊一些其用到的知識,不然的話,不能到時候懵懵懂懂的。接下來看看這個@Import這個注解,他是來干什么的呢?
@Import注釋,根據字義,我們可以理解為導入組件選擇器自動配置,作用是將需要導入的組件以全類名的方式返回,這些組件將被添加到Spring容器中,如圖:
自動配置類的作用,配置注入功能組件自動完成。
a.允許使用@Configuration注解的類
這個比較簡單,如果明確知道需要引入哪個配置類,直接引入就可以。
b.允許是實現ImportSelector接口的類
如果並不確定引入哪個配置類,需要根據@Import注解所標識的類或者另一個注解(通常是注解)里的定義信息選擇配置類的話,用這種方式。
c.Spring會把實現ImportSelector接口的類中的SelectImport方法返回的值注入到Spring容器中(即IOC容器)。這個方法的返回值必須是一個class的全類名的String[]。
比較典型的注解:
好吧,簡單了解這個之后,下面來談談重點吧:
@EnableAutoConfiguration剛才已經貼了一張圖了,點開源碼之后就是
繼續點擊進去,你會發現:
一路點下去,發現AutoConfigurationImportSelector實現了父類接口ImportSelector,並且重寫了父類的selectImports的方法。
在這個方法中,其實主要實現是看configurations這個里面返回的是什么東西就行了。
繼續點進去這個方法
繼續點擊進去
這個時候,我們就豁然開朗了,原來最終我們是要找“META-INF/spring.factories”這個配置文件啊!
但是這個東西實在哪里呢,來看看我們引入的mavenjar包吧。
原來是這個東西,看看里面是什么
是不是明白了,***AutoConfiguration,這個不就是我們上面的注解的形式嗎?
上圖里面這么多的xxxAutoConfiguration就是我們的這么久得出的結果,最終就是加載這么多的類的全路徑,然后springboot內部就是實例化這些類並加載到容器里面,完成springboot應用啟動時的自動配置。
來看看下面這樣圖,他們大神畫的生動的圖,讓人豁然開朗:
自動配置幕后英雄:SpringFactoriesLoader詳解
借助於Spring框架原有的一個工具類:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自動配置功效才得以大功告成!
SpringFactoriesLoader屬於Spring框架私有的一種擴展方案,其主要功能就是從指定的配置文件META-INF/spring.factories加載配置。
public abstract class SpringFactoriesLoader { //... public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) { ... } public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { .... } }
配合@EnableAutoConfiguration使用的話,它更多是提供一種配置查找的功能支持,即根據@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration作為查找的Key,獲取對應的一組@Configuration類
上圖就是從SpringBoot的autoconfigure依賴包中的META-INF/spring.factories配置文件中摘錄的一段內容,可以很好地說明問題。
所以,@EnableAutoConfiguration自動配置的魔法騎士就變成了:從classpath中搜尋所有的META-INF/spring.factories配置文件,並將其中org.springframework.boot.autoconfigure.EnableutoConfiguration對應的配置項通過反射(Java Refletion)實例化為對應的標注了@Configuration的JavaConfig形式的IoC容器配置類,然后匯總為一個並加載到IoC容器。
在來探討一個問題:springboot的啟動類其實基本上就兩部分,一部分是spring-boot-starter,另一部分是spring-boot-autoconfig,starter里面其實沒有什么東西,就是依賴了autoconfig。
這么多的***AutoConfiguration,點進去過去會自動調到對應的啟動類源碼,在啟動類中還有一個配置類***Properties,這個是默認自帶的,例如:這個是activemq的默認配置類(默認的配置類)
在這里,ActiveMQAutoConfiguration的源碼會關聯這個配置文件,這樣他們就可以連在一起,最直接的體現就是,我們在application.properties或者applciation.yml中可以提示配置文件的屬性
這樣的話,整個啟動類的思路就比較清晰了。
總結:
@ConditionalOnXX 條件判斷注解,通過條件來判斷這個類是否生效!
隨便拿一個啟動器舉例子:
1:springboot啟動的時候會加載大量的自動配置類。spring.factories
2:我們就需要判斷我們的類是否在這個里面,如果不存在,我們需要手動導入,如果存在,導入啟動器即可。
3:我們的配置文件(application.yml或者application.properties)之所以可以自動配置生效:
(1)xxxAutoConfiguation:自動配置類,根據條件@ConditionalOnXX來進行判斷是否生效,如果生效則成功注入bean
(2)xxxProperties:封裝配置文件的相關屬性
4:給容器中自動配置屬性的時候,會通過xxxProperties來獲取某用戶的配置文件中的屬性,如果沒有則使用默認,有則使用自己配置的。
(未完待續........)
本文引用文章聲明:
原文鏈接:https://blog.csdn.net/neulily2005/article/details/83750027 -------CSDN博主「Mr.甘」
原文鏈接:https://blog.csdn.net/mapleleafforest/article/details/86623578----------CSDN博主「a maple leaf」
原文鏈接:https://www.cnblogs.com/shamo89/p/8184960.html------那啥快看[博客園]
原文鏈接:http://www.imooc.com/article/275453?block_id=tuijian_wz---Java3y[慕課網]