<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          淘寶一面:“說(shuō)一下 Spring Boot 自動(dòng)裝配原理唄?”

          共 6045字,需瀏覽 13分鐘

           ·

          2021-01-30 23:49

          每次問(wèn)到 Spring Boot, 面試官非常喜歡問(wèn)這個(gè)問(wèn)題:“講述一下 SpringBoot 自動(dòng)裝配原理?”。

          我覺(jué)得我們可以從以下幾個(gè)方面回答:

          1. 什么是 SpringBoot 自動(dòng)裝配?
          2. SpringBoot 是如何實(shí)現(xiàn)自動(dòng)裝配的?如何實(shí)現(xiàn)按需加載?
          3. 如何實(shí)現(xiàn)一個(gè) Starter?

          前言

          使用過(guò) Spring 的小伙伴,一定有被 XML 配置統(tǒng)治的恐懼。即使 Spring 后面引入了基于注解的配置,我們?cè)陂_(kāi)啟某些 Spring 特性或者引入第三方依賴的時(shí)候,還是需要用 XML 或 Java 進(jìn)行顯式配置。

          舉個(gè)例子。沒(méi)有 Spring Boot 的時(shí)候,我們寫(xiě)一個(gè) RestFul Web 服務(wù),還首先需要進(jìn)行如下配置。

          27672985d7f77257f714dd687ab90d09.webp

          spring-servlet.xml

          177799c6f8e681593da99c43652ab0c2.webp

          但是,Spring Boot 項(xiàng)目,我們只需要添加相關(guān)依賴,無(wú)需配置,通過(guò)啟動(dòng)下面的 main 方法即可。

          ff75893a36c40fb8ce4a2711c71656e9.webp

          并且,我們通過(guò) Spring Boot 的全局配置文件 application.propertiesapplication.yml即可對(duì)項(xiàng)目進(jìn)行設(shè)置比如更換端口號(hào),配置 JPA 屬性等等。

          為什么 Spring Boot 使用起來(lái)這么酸爽呢? 這得益于其自動(dòng)裝配。自動(dòng)裝配可以說(shuō)是 Spring Boot 的核心,那究竟什么是自動(dòng)裝配呢?

          什么是 SpringBoot 自動(dòng)裝配?

          我們現(xiàn)在提到自動(dòng)裝配的時(shí)候,一般會(huì)和 Spring Boot 聯(lián)系在一起。但是,實(shí)際上 Spring Framework 早就實(shí)現(xiàn)了這個(gè)功能。Spring Boot 只是在其基礎(chǔ)上,通過(guò) SPI 的方式,做了進(jìn)一步優(yōu)化。

          SpringBoot 定義了一套接口規(guī)范,這套規(guī)范規(guī)定:SpringBoot 在啟動(dòng)時(shí)會(huì)掃描外部引用 jar 包中的META-INF/spring.factories文件,將文件中配置的類型信息加載到 Spring 容器(此處涉及到 JVM 類加載機(jī)制與 Spring 的容器知識(shí)),并執(zhí)行類中定義的各種操作。對(duì)于外部 jar 來(lái)說(shuō),只需要按照 SpringBoot 定義的標(biāo)準(zhǔn),就能將自己的功能裝置進(jìn) SpringBoot。

          沒(méi)有 Spring Boot 的情況下,如果我們需要引入第三方依賴,需要手動(dòng)配置,非常麻煩。但是,Spring Boot 中,我們直接引入一個(gè) starter 即可。比如你想要在項(xiàng)目中使用 redis 的話,直接在項(xiàng)目中引入對(duì)應(yīng)的 starter 即可。

          02e519988ad91bb42688c1f8a2e70588.webp

          引入 starter 之后,我們通過(guò)少量注解和一些簡(jiǎn)單的配置就能使用第三方組件提供的功能了。

          在我看來(lái),自動(dòng)裝配可以簡(jiǎn)單理解為:通過(guò)注解或者一些簡(jiǎn)單的配置就能在 Spring Boot 的幫助下實(shí)現(xiàn)某塊功能。

          SpringBoot 是如何實(shí)現(xiàn)自動(dòng)裝配的?

          我們先看一下 SpringBoot 的核心注解 SpringBootApplication

          e789d1cce0d62dee8cc1387e41dcd8dd.webp

          大概可以把 @SpringBootApplication看作是 @Configuration@EnableAutoConfiguration@ComponentScan 注解的集合。根據(jù) SpringBoot 官網(wǎng),這三個(gè)注解的作用分別是:

          • @EnableAutoConfiguration:?jiǎn)⒂?SpringBoot 的自動(dòng)配置機(jī)制
          • @Configuration:允許在上下文中注冊(cè)額外的 bean 或?qū)肫渌渲妙?/li>
          • @ComponentScan:掃描被@Component (@Service,@Controller)注解的 bean,注解默認(rèn)會(huì)掃描啟動(dòng)類所在的包下所有的類 ,可以自定義不掃描某些 bean。如下圖所示,容器中將排除TypeExcludeFilterAutoConfigurationExcludeFilter
          2563cbdc37a505aa9f2761f9ebb8eb6e.webp@EnableAutoConfiguration?是實(shí)現(xiàn)自動(dòng)裝配的重要注解,我們以這個(gè)注解入手。

          @EnableAutoConfiguration:實(shí)現(xiàn)自動(dòng)裝配的核心注解

          EnableAutoConfiguration?只是一個(gè)簡(jiǎn)單地注解,自動(dòng)裝配核心功能的實(shí)現(xiàn)實(shí)際是通過(guò)?AutoConfigurationImportSelector類。

          a33c27c4cff8a02842c01ef7958b0a07.webp

          我們現(xiàn)在重點(diǎn)分析下AutoConfigurationImportSelector 類到底做了什么?

          AutoConfigurationImportSelector:加載自動(dòng)裝配類

          AutoConfigurationImportSelector類的繼承體系如下:

          8f966887d7ad07defeb8bbc6d9d9b922.webp

          可以看出,AutoConfigurationImportSelector 類實(shí)現(xiàn)了 ImportSelector接口,也就實(shí)現(xiàn)了這個(gè)接口中的 selectImports方法,該方法主要用于獲取所有符合條件的類的全限定類名,這些類需要被加載到 IoC 容器中

          private?static?final?String[]?NO_IMPORTS?=?new?String[0];

          public?String[]?selectImports(AnnotationMetadata?annotationMetadata)?{
          ????????//?<1>.判斷自動(dòng)裝配開(kāi)關(guān)是否打開(kāi)
          ????????if?(!this.isEnabled(annotationMetadata))?{
          ????????????return?NO_IMPORTS;
          ????????}?else?{
          ??????????//<2>.獲取所有需要裝配的bean
          ????????????AutoConfigurationMetadata?autoConfigurationMetadata?=?AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
          ????????????AutoConfigurationImportSelector.AutoConfigurationEntry?autoConfigurationEntry?=?this.getAutoConfigurationEntry(autoConfigurationMetadata,?annotationMetadata);
          ????????????return?StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
          ????????}
          ????}

          這里我們需要重點(diǎn)關(guān)注一下getAutoConfigurationEntry()方法,這個(gè)方法主要負(fù)責(zé)加載自動(dòng)配置類的。

          該方法調(diào)用鏈如下:

          4f04cd329dd978828d4fb4faf0efb625.webp

          現(xiàn)在我們結(jié)合getAutoConfigurationEntry()的源碼來(lái)詳細(xì)分析一下:

          private?static?final?AutoConfigurationEntry?EMPTY_ENTRY?=?new?AutoConfigurationEntry();

          AutoConfigurationEntry?getAutoConfigurationEntry(AutoConfigurationMetadata?autoConfigurationMetadata,?AnnotationMetadata?annotationMetadata)?{
          ????????//<1>.
          ????????if?(!this.isEnabled(annotationMetadata))?{
          ????????????return?EMPTY_ENTRY;
          ????????}?else?{
          ????????????//<2>.
          ????????????AnnotationAttributes?attributes?=?this.getAttributes(annotationMetadata);
          ????????????//<3>.
          ????????????List?configurations?=?this.getCandidateConfigurations(annotationMetadata,?attributes);
          ????????????//<4>.
          ????????????configurations?=?this.removeDuplicates(configurations);
          ????????????Set?exclusions?=?this.getExclusions(annotationMetadata,?attributes);
          ????????????this.checkExcludedClasses(configurations,?exclusions);
          ????????????configurations.removeAll(exclusions);
          ????????????configurations?=?this.filter(configurations,?autoConfigurationMetadata);
          ????????????this.fireAutoConfigurationImportEvents(configurations,?exclusions);
          ????????????return?new?AutoConfigurationImportSelector.AutoConfigurationEntry(configurations,?exclusions);
          ????????}
          ????}

          第 1 步:

          判斷自動(dòng)裝配開(kāi)關(guān)是否打開(kāi)。默認(rèn)spring.boot.enableautoconfiguration=true,可在 application.propertiesapplication.yml 中設(shè)置

          61e80cdaee6a52068063dcafa9547c29.webp

          第 2 步

          用于獲取EnableAutoConfiguration注解中的 excludeexcludeName

          a63515d0be23000518cd3aecfc06d699.webp

          第 3 步

          獲取需要自動(dòng)裝配的所有配置類,讀取META-INF/spring.factories

          spring-boot/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

          b7a2019d9fadcdf37c90805aa421a361.webp

          從下圖可以看到這個(gè)文件的配置內(nèi)容都被我們讀取到了。XXXAutoConfiguration的作用就是按需加載組件。

          9e8df93edafc728d02db4e39087af19e.webp

          不光是這個(gè)依賴下的META-INF/spring.factories被讀取到,所有 Spring Boot Starter 下的META-INF/spring.factories都會(huì)被讀取到。

          所以,你可以清楚滴看到, druid 數(shù)據(jù)庫(kù)連接池的 Spring Boot Starter 就創(chuàng)建了META-INF/spring.factories文件。

          如果,我們自己要?jiǎng)?chuàng)建一個(gè) Spring Boot Starter,這一步是必不可少的。

          2c79dd2d5fe029a3584c0f07c66a8e3a.webp

          第 4 步

          到這里可能面試官會(huì)問(wèn)你:“spring.factories中這么多配置,每次啟動(dòng)都要全部加載么?”。

          很明顯,這是不現(xiàn)實(shí)的。我們 debug 到后面你會(huì)發(fā)現(xiàn),configurations 的值變小了。

          8d6c2e9fa43b8a049aa85d51f898f797.webp

          因?yàn)椋@一步有經(jīng)歷了一遍篩選,@ConditionalOnXXX 中的所有條件都滿足,該類才會(huì)生效。

          @Configuration
          //?檢查相關(guān)的類:RabbitTemplate 和 Channel是否存在
          //?存在才會(huì)加載
          @ConditionalOnClass({?RabbitTemplate.class,?Channel.class?})
          @EnableConfigurationProperties(RabbitProperties.class)
          @Import(RabbitAnnotationDrivenConfiguration.class)
          public?class?RabbitAutoConfiguration?
          {
          }

          有興趣的童鞋可以詳細(xì)了解下 Spring Boot 提供的條件注解

          • @ConditionalOnBean:當(dāng)容器里有指定 Bean 的條件下
          • @ConditionalOnMissingBean:當(dāng)容器里沒(méi)有指定 Bean 的情況下
          • @ConditionalOnSingleCandidate:當(dāng)指定 Bean 在容器中只有一個(gè),或者雖然有多個(gè)但是指定首選 Bean
          • @ConditionalOnClass:當(dāng)類路徑下有指定類的條件下
          • @ConditionalOnMissingClass:當(dāng)類路徑下沒(méi)有指定類的條件下
          • @ConditionalOnProperty:指定的屬性是否有指定的值
          • @ConditionalOnResource:類路徑是否有指定的值
          • @ConditionalOnExpression:基于 SpEL 表達(dá)式作為判斷條件
          • @ConditionalOnJava:基于 Java 版本作為判斷條件
          • @ConditionalOnJndi:在 JNDI 存在的條件下差在指定的位置
          • @ConditionalOnNotWebApplication:當(dāng)前項(xiàng)目不是 Web 項(xiàng)目的條件下
          • @ConditionalOnWebApplication:當(dāng)前項(xiàng)目是 Web 項(xiàng) 目的條件下

          如何實(shí)現(xiàn)一個(gè) Starter

          光說(shuō)不練假把式,現(xiàn)在就來(lái)擼一個(gè) starter,實(shí)現(xiàn)自定義線程池

          第一步,創(chuàng)建threadpool-spring-boot-starter工程

          25a126a199a143473fff1825d3a49c30.webp

          第二步,引入 Spring Boot 相關(guān)依賴

          c4d3fa5cdd848690109bb80fd43664c8.webp

          第三步,創(chuàng)建ThreadPoolAutoConfiguration

          963602de1af5b127ebec135d11754622.webp

          第四步,在threadpool-spring-boot-starter工程的 resources 包下創(chuàng)建META-INF/spring.factories文件

          dbc3385db66a9e35c176f073214c7027.webp

          最后新建工程引入threadpool-spring-boot-starter

          b2a419cceca6350b11c293a8cb0d0462.webp

          測(cè)試通過(guò)!!!

          42d82370fea20a0e530960c80ed442f3.webp

          總結(jié)

          Spring Boot 通過(guò)@EnableAutoConfiguration開(kāi)啟自動(dòng)裝配,通過(guò) SpringFactoriesLoader 最終加載META-INF/spring.factories中的自動(dòng)配置類實(shí)現(xiàn)自動(dòng)裝配,自動(dòng)配置類其實(shí)就是通過(guò)@Conditional按需加載的配置類,想要其生效必須引入spring-boot-starter-xxx包實(shí)現(xiàn)起步依賴

          推薦??: ?Github掘金計(jì)劃:Github上的一些優(yōu)質(zhì)項(xiàng)目搜羅

          推薦??:V4.0 《JavaGuide 面試突擊版》來(lái)啦!年初搞波大的

          推薦??:Github,永遠(yuǎn)滴神?

          我是Guide哥,Java后端開(kāi)發(fā),擁抱開(kāi)源,喜歡烹飪,自由的少年。一個(gè)喜歡分享大廠面試真題的技術(shù)人。我們下期再見(jiàn)!

          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  麻豆传媒在线一级二级 | 五月丁香乱伦 | 亚洲无码专区区免费 | 激情综合婷婷 | 国产精品伦理一区 |