小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

淘寶一面:“講述一下 SpringBoot 自動裝配原理?”

 Laterede0fo33i 2021-01-30

每次問到 Spring Boot, 面試官非常喜歡問這個問題:“講述一下 SpringBoot 自動裝配原理?”。

我覺得我們可以從以下幾個方面回答:

  1. 什么是 SpringBoot 自動裝配?

  2. SpringBoot 是如何實現(xiàn)自動裝配的?如何實現(xiàn)按需加載?

  3. 如何實現(xiàn)一個 Starter?

篇幅問題,這篇文章并沒有深入,小伙伴們也可以直接使用 debug 的方式去看看 SpringBoot 自動裝配部分的源代碼。

前言

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

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

@Configurationpublic class RESTConfiguration{    @Bean
    public View jsonTemplate() {
        MappingJackson2JsonView view = new MappingJackson2JsonView();
        view.setPrettyPrint(true);        return view;
    }    @Bean
    public ViewResolver viewResolver() {        return new BeanNameViewResolver();
    }
}

spring-servlet.xml

<beans xmlns="http://www./schema/beans"
    xmlns:xsi="http://www./2001/XMLSchema-instance" xmlns:context="http://www./schema/context"
    xmlns:mvc="http://www./schema/mvc"
    xsi:schemaLocation="http://www./schema/beans http://www./schema/beans/spring-beans.xsd
    http://www./schema/context/ http://www./schema/context/spring-context.xsd
    http://www./schema/mvc/ http://www./schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.howtodoinjava.demo" />
    <mvc:annotation-driven />

    <!-- JSON Support -->
    <bean name="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
    <bean name="jsonTemplate" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/></beans>

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

@SpringBootApplicationpublic class DemoApplication {    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

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

為什么 Spring Boot 使用起來這么酸爽呢? 這得益于其自動裝配。自動裝配可以說是 Spring Boot 的核心,那究竟什么是自動裝配呢?

什么是 SpringBoot 自動裝配?

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

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

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

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

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

SpringBoot 是如何實現(xiàn)自動裝配的?

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

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited<1.>@SpringBootConfiguration<2.>@ComponentScan<3.>@EnableAutoConfigurationpublic @interface SpringBootApplication {

}@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration //實際上它也是一個配置類public @interface SpringBootConfiguration {
}

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

  • @EnableAutoConfiguration:啟用 SpringBoot 的自動配置機(jī)制

  • @Configuration:允許在上下文中注冊額外的 bean 或?qū)肫渌渲妙?/p>

  • @ComponentScan: 掃描被@Component (@Service,@Controller)注解的 bean,注解默認(rèn)會掃描啟動類所在的包下所有的類 ,可以自定義不掃描某些 bean。如下圖所示,容器中將排除TypeExcludeFilterAutoConfigurationExcludeFilter。

@EnableAutoConfiguration 是實現(xiàn)自動裝配的重要注解,我們以這個注解入手。

@EnableAutoConfiguration:實現(xiàn)自動裝配的核心注解

EnableAutoConfiguration 只是一個簡單地注解,自動裝配核心功能的實現(xiàn)實際是通過 AutoConfigurationImportSelector類。

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage //作用:將main包下的所欲組件注冊到容器中@Import({AutoConfigurationImportSelector.class}) //加載自動裝配類 xxxAutoconfigurationpublic @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

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

AutoConfigurationImportSelector:加載自動裝配類

AutoConfigurationImportSelector類的繼承體系如下:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}public interface DeferredImportSelector extends ImportSelector {

}public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

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

private static final String[] NO_IMPORTS = new String[0];public String[] selectImports(AnnotationMetadata annotationMetadata) {        // <1>.判斷自動裝配開關(guān)是否打開
        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()方法,這個方法主要負(fù)責(zé)加載自動配置類的。

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

現(xiàn)在我們結(jié)合getAutoConfigurationEntry()的源碼來詳細(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<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);            //<4>.
            configurations = this.removeDuplicates(configurations);
            Set<String> 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 步:

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

第 2 步

用于獲取EnableAutoConfiguration注解中的 excludeexcludeName。

第 3 步

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

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

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

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

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

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

第 4 步

到這里可能面試官會問你:“spring.factories中這么多站長博客配置,每次啟動都要全部加載么?”。

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

因為,這一步有經(jīng)歷了一遍篩選,@ConditionalOnXXX 中的所有條件都滿足,該類才會生效。

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

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

  • @ConditionalOnBean:當(dāng)容器里有指定 Bean 的條件下

  • @ConditionalOnMissingBean:當(dāng)容器里沒有指定 Bean 的情況下

  • @ConditionalOnSingleCandidate:當(dāng)指定 Bean 在容器中只有一個,或者雖然有多個但是指定首選 Bean

  • @ConditionalOnClass:當(dāng)類路徑下有指定類的條件下

  • @ConditionalOnMissingClass:當(dāng)類路徑下沒有指定類的條件下

  • @ConditionalOnProperty:指定的屬性是否有指定的值

  • @ConditionalOnResource:類路徑是否有指定的值

  • @ConditionalOnExpression:基于 SpEL 表達(dá)式作為判斷條件

  • @ConditionalOnJava:基于 Java 版本作為判斷條件

  • @ConditionalOnJndi:在 JNDI 存在的條件下差在指定的位置

  • @ConditionalOnNotWebApplication:當(dāng)前項目不是 Web 項目的條件下

  • @ConditionalOnWebApplication:當(dāng)前項目是 Web 項 目的條件下

如何實現(xiàn)一個 Starter

光說不練假把式,現(xiàn)在就來擼一個 starter,實現(xiàn)自定義線程池

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

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

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

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

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

測試通過?。。?/p>

總結(jié)

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

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多