點選上方“Java知音”,選擇“置頂公衆号”
技術文章第一時間送達!
作者:MyBug
juejin.im/post/5d25b7115188256cd02a00ad
推薦閱讀(點選即可跳轉閱讀)
1. SpringBoot内容聚合
2. 面試題内容聚合
3. 設計模式内容聚合
4. Mybatis内容聚合
5. 多線程内容聚合
一、前言
使用過springboot的同學應該已經知道,springboot通過預設配置了很多架構的使用方式幫我們大大簡化了項目初始搭建以及開發過程。
本文的目的就是一步步分析springboot的啟動過程,這次主要是分析springboot特性自動裝配。
那麼首先帶領大家回顧一下以往我們的web項目是如何搭建的,通常我們要搭建一個基于Spring的Web應用,我們需要做以下一些工作:
- pom檔案中引入相關jar包,包括spring、springmvc、redis、mybaits、log4j、mysql-connector-java 等等相關jar …
- 配置web.xml,Listener配置、Filter配置、Servlet配置、log4j配置、error配置 …
- 配置資料庫連接配接、配置spring事務
- 配置視圖解析器
- 開啟注解、自動掃描功能
- 配置完成後部署tomcat、啟動調試……
花在搭建一個初始項目,可能一個小時就過去了或者半天救過了,但是用了SpringBoot之後一切都會變得非常便捷,下面我們首先來分析一下SpringBoot的起步依賴以及自動配置。
二、起步依賴
1.在我們的pom檔案裡面引入以下jar:
<modelVersion>4.0.0
- spring-boot-starter-web包自動幫我們引入了web子產品開發需要的相關jar包。
- mybatis-spring-boot-starter幫我們引入了dao開發相關的jar包。
- spring-boot-starter-xxx是官方提供的starter,xxx-spring-boot-starter是第三方提供的starter。
截圖看一下我們的mybatis-spring-boot-starter
可以看出mybatis-spring-boot-starter并沒有任何源碼,隻有一個pom檔案,它的作用就是幫我們引入其它jar。
2.配置資料源
stater機制幫我們完成了項目起步所需要的的相關jar包。那問題又來了,傳統的spring應用中不是要在application.xml中配置很多bean的嗎,比如dataSource的配置,transactionManager的配置 … springboot是如何幫我們完成這些bean的配置的?
下面我們來分析這個過程
三、自動配置
1.基于java代碼的bean配置
以mybatis為例,在上面的截圖中,我們發現mybatis-spring-boot-starter這個包幫我們引入了mybatis-spring-boot-autoconfigure這個包,如下圖:
裡面有MybatisAutoConfiguration這個類,打開這個類看看有些什麼東西。
熟悉@Configuration&、@Bean這兩個bean的同學或許已經知道了。這兩個注解一起使用就可以建立一個基于java代碼的配置類,可以用來替代相應的xml配置檔案。
@Configuration注解的類可以看作是能生産讓Spring IoC容器管理的Bean執行個體的工廠。@Bean注解告訴Spring,一個帶有@Bean的注解方法将傳回一個對象,該對象應該被注冊到spring容器中。
是以上面的MybatisAutoConfiguration這個類,自動幫我們生成了SqlSessionFactory這些Mybatis的重要執行個體并交給spring容器管理,進而完成bean的自動注冊。
2.自動配置條件依賴
從MybatisAutoConfiguration這個類中使用的注解可以看出,要完成自動配置是有依賴條件的。
@org.springframework.context.
首先預習一下Springboot是常用的條件依賴注解有:
- @ConditionalOnBean,僅在目前上下文中存在某個bean時,才會執行個體化這個Bean。
- @ConditionalOnClass,某個class位于類路徑上,才會執行個體化這個Bean。
- @ConditionalOnExpression,當表達式為true的時候,才會執行個體化這個Bean。
- @ConditionalOnMissingBean,僅在目前上下文中不存在某個bean時,才會執行個體化這個Bean。
- @ConditionalOnMissingClass,某個class在類路徑上不存在的時候,才會執行個體化這個Bean。
- @ConditionalOnNotWebApplication,不是web應用時才會執行個體化這個Bean。
- @AutoConfigureAfter,在某個bean完成自動配置後執行個體化這個bean。
- @AutoConfigureBefore,在某個bean完成自動配置前執行個體化這個bean。
是以要完成Mybatis的自動配置,需要在類路徑中存在SqlSessionFactory.class、SqlSessionFactoryBean.class這兩個類,需要存在DataSource這個bean且這個bean完成自動注冊。
進入DataSourceAutoConfiguration這個類,可以看到這個類屬于這個包:
org.springframework.boot.autoconfigure.jdbc
這個包又屬于spring-boot-autoconfigure-2.0.4.RELEASE.jar這個包,自動配置這個包幫們引入了jdbc、kafka、logging、mail、mongo等包。很多包需要我們引入相應jar後自動配置才生效。
3.Bean參數的擷取
到此我們已經知道了bean的配置過程,但是還沒有看到springboot是如何讀取yml或者properites配置檔案的的屬性來建立資料源的?
在DataSourceAutoConfiguration類裡面,我們注意到使用了EnableConfigurationProperties這個注解。
@Configuration
DataSourceProperties中封裝了資料源的各個屬性,且使用了注解ConfigurationProperties指定了配置檔案的字首。
"spring.datasource")
通過以上分析,我們可以得知:
@ConfigurationProperties注解的作用是把yml或者properties配置檔案轉化為bean。
@EnableConfigurationProperties注解的作用是使@ConfigurationProperties注解生效。如果隻配置@ConfigurationProperties注解,在spring容器中是擷取不到yml或者properties配置檔案轉化的bean的。
通過這種方式,把yml或者properties配置參數轉化為bean,這些bean又是如何被發現與加載的?
4.Bean的發現
springboot預設掃描啟動類所在的包下的主類與子類的所有元件,但并沒有包括依賴包的中的類,那麼依賴包中的bean是如何被發現和加載的?
我們通常在啟動類中加@SpringBootApplication這個注解,點進去看
@Target(ElementType.TYPE)
實際上重要的隻有三個Annotation:
- @Configuration(@SpringBootConfiguration裡面還是應用了@Configuration)
- @EnableAutoConfiguration
- @ComponentScan
@Configuration的作用上面我們已經知道了,被注解的類将成為一個bean配置類。
@ComponentScan的作用就是自動掃描并加載符合條件的元件,比如@Component和@Repository等,最終将這些bean定義加載到spring容器中。
@EnableAutoConfiguration 這個注解的功能很重要,借助@Import的支援,收集和注冊依賴包中相關的bean定義。
@Target(ElementType.TYPE)
如上源碼,@EnableAutoConfiguration注解引入了@AutoConfigurationPackage和@Import這兩個注解。@AutoConfigurationPackage的作用就是自動配置的包,@Import導入需要自動配置的元件。
@Target(ElementType.TYPE)
~
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
*/
new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()
new AutoConfigurationPackages.PackageImport(metadata)
這兩句代碼的作用就是加載啟動類所在的包下的主類與子類的所有元件注冊到spring容器,這就是前文所說的springboot預設掃描啟動類所在的包下的主類與子類的所有元件。
那問題又來了,要搜集并注冊到spring容器的那些beans來自哪裡?
進入 AutoConfigurationImportSelector類,我們可以發現SpringFactoriesLoader.loadFactoryNames方法調用loadSpringFactories方法從所有的jar包中讀取META-INF/spring.factories檔案資訊。
下面是spring-boot-autoconfigure這個jar中spring.factories檔案部分内容,其中有一個key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的值定義了需要自動配置的bean,通過讀取這個配置擷取一組@Configuration類。
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
每個xxxAutoConfiguration都是一個基于java的bean配置類。實際上,這些xxxAutoConfiguratio不是所有都會被加載,會根據xxxAutoConfiguration上的@ConditionalOnClass等條件判斷是否加載;通過反射機制将spring.factories中@Configuration類執行個體化為對應的java實列。
到此我們已經知道怎麼發現要自動配置的bean了,最後一步就是怎麼樣将這些bean加載到spring容器。
5.Bean 加載
如果要讓一個普通類交給Spring容器管理,通常有以下方法:
- 使用 @Configuration與@Bean 注解
- 使用@Controller @Service @Repository @Component 注解标注該類,然後啟用@ComponentScan自動掃描
- 使用@Import 方法
springboot中使用了@Import 方法
@EnableAutoConfiguration注解中使用了@Import({AutoConfigurationImportSelector.class})注解,AutoConfigurationImportSelector實作了DeferredImportSelector接口,
DeferredImportSelector接口繼承了ImportSelector接口,ImportSelector接口隻有一個selectImports方法。
selectImports方法傳回一組bean,@EnableAutoConfiguration注解借助@Import注解将這組bean注入到spring容器中,springboot正式通過這種機制來完成bean的注入的。
四、總結
我們可以将自動配置的關鍵幾步以及相應的注解總結如下:
- @Configuration&與@Bean------>>>基于java代碼的bean配置
- @Conditional-------->>>>>>設定自動配置條件依賴
- @EnableConfigurationProperties與@ConfigurationProperties->讀取配置檔案轉換為bean。
- @EnableAutoConfiguration、@AutoConfigurationPackage 與@Import->實作bean發現與加載。
覺得不錯?歡迎轉發分享給更多人