亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

springboot自動(dòng)配置原理以及spring.factories文件的作用詳解

 更新時(shí)間:2021年10月08日 09:43:49   作者:工人最光榮  
這篇文章主要介紹了springboot自動(dòng)配置原理以及spring.factories文件的作用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、springboot 自動(dòng)配置原理

先說(shuō)說(shuō)我們自己的應(yīng)用程序中Bean加入容器的辦法:

package com.ynunicom.dc.dingdingcontractapp;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * @author jinye.Bai
 */
@SpringBootApplication(
        scanBasePackages ={"com.ynunicom.dc.dingdingcontractapp"}
)
public class DingdingContractAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(DingdingContractAppApplication.class, args);
    }
}

我們?cè)趹?yīng)用程序的入口設(shè)置了 @SpringBootApplication標(biāo)簽,默認(rèn)情況下他會(huì)掃描所有次級(jí)目錄。

如果增加了 scanBasePackages屬性,就會(huì)掃描所有被指定的路徑及其次級(jí)目錄。

那么它在掃描的是什么東西呢?

是這個(gè):@Component

所有被掃描到的 @Component,都會(huì)成為一個(gè)默認(rèn)的singleton(單例,即一個(gè)容器里只有一個(gè)對(duì)象實(shí)體)加入到容器中。

認(rèn)識(shí)到以上這點(diǎn),便于我們理解springboot自動(dòng)配置的機(jī)制。

接下來(lái)讓我們看看在自己的應(yīng)用程序中實(shí)現(xiàn)配置的方法。

如圖:

package com.ynunicom.dc.dingdingcontractapp.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
 * @author: jinye.Bai
 * @date: 2020/5/22 15:51
 */
@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}

這里我們?cè)O(shè)置了一個(gè)配置,往容器中加入了一個(gè)RestTemplate。

首先說(shuō) @Configuration,這個(gè)標(biāo)簽繼承了 @Component標(biāo)簽,我們可以在標(biāo)簽內(nèi)容看到:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

可以看到其中是有 @Component標(biāo)簽的,所以,@Configuration會(huì)被 @SpringBootApplication掃描到,進(jìn)而把它和它下面的 @Bean加入容器,于是我們 RestTemplate的內(nèi)容就配置完成了,在后續(xù)的使用中,我們就可以直接從容器中拿出RestTemplate使用它。

對(duì)于在maven中引用的其他外部包加入容器的過(guò)程,需要用到spring.factories。

二、spring.factories文件的作用

在springboot運(yùn)行時(shí),SpringFactoriesLoader 類會(huì)去尋找

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

我們以mybatis-plus為例。

首先我們引入:

  <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>

然后去maven的依賴?yán)锟此淖詣?dòng)配置類MybatisPlusAutoConfiguration

@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisPlusProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {

可以看到有上文提到的 @Configuration,還有從application.yml載入自動(dòng)配置的 @EnableConfigurationProperties({MybatisPlusProperties.class})

這個(gè)注解的具體內(nèi)容請(qǐng)查看我另一篇博文,對(duì)其進(jìn)行了解釋:

迅速學(xué)會(huì)@ConfigurationProperties的使用

也就是說(shuō),springboot只要能掃描到MybatisPlusAutoConfiguration類的 @Configuration注解,其中的所有配置就能自動(dòng)加入到容器中,這一過(guò)程由上面提到的SpringFactoriesLoader 起作用,它會(huì)去尋找 “META-INF/spring.factories” 文件,我們可以在 mybatis-plus的依賴中找到它:

spring.factories被SpringFactoriesLoader找到

SpringFactoriesLoader為什么要讀取它呢?因?yàn)樗鼉?nèi)部是這樣的

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
  com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

spring.factories用鍵值對(duì)的方式記錄了所有需要加入容器的類,EnableAutoConfigurationImportSelector的selectImports方法返回的類名,來(lái)自spring.factories文件內(nèi)的配置信息,這些配置信息的key等于EnableAutoConfiguration,因?yàn)閟pring boot應(yīng)用啟動(dòng)時(shí)使用了EnableAutoConfiguration注解,所以EnableAutoConfiguration注解通過(guò)import注解將EnableAutoConfigurationImportSelector類實(shí)例化,并且將其selectImports方法返回的類名實(shí)例化后注冊(cè)到spring容器。

以上內(nèi)容是springboot獲得這些類的方式,如果你想要實(shí)現(xiàn)自己的自動(dòng)配置,就將你的類通過(guò)鍵值對(duì)的方式寫在你的spring.factories即可,注意,值是你的自動(dòng)配置類,鍵必須是org.springframework.boot.autoconfigure.EnableAutoConfiguration

spring.factories 的妙用

現(xiàn)象

在閱讀 Spring-Boot 相關(guān)源碼時(shí),常常見(jiàn)到 spring.factories 文件,里面寫了自動(dòng)配置(AutoConfiguration)相關(guān)的類名,因此產(chǎn)生了一個(gè)疑問(wèn):“明明自動(dòng)配置的類已經(jīng)打上了 @Configuration 的注解,為什么還要寫 spring.factories 文件?

用過(guò) Spring Boot 的都知道

@ComponentScan 注解的作用是掃描 @SpringBootApplication 所在的 Application 類所在的包(basepackage)下所有的 @component 注解(或拓展了 @component 的注解)標(biāo)記的 bean,并注冊(cè)到 spring 容器中。

那么問(wèn)題來(lái)了

在 Spring Boot 項(xiàng)目中,如果你想要被 Spring 容器管理的 bean 不在 Spring Boot 包掃描路徑下,怎么辦?

解決 Spring Boot 中不能被默認(rèn)路徑掃描的配置類的方式,有 2 種:

(1)在 Spring Boot 主類上使用 @Import 注解

(2)使用 spring.factories 文件

以下是對(duì) 使用 spring.factories 文件的簡(jiǎn)單理解

Spring Boot 的擴(kuò)展機(jī)制之 Spring Factories

Spring Boot 中有一種非常解耦的擴(kuò)展機(jī)制:Spring Factories。這種擴(kuò)展機(jī)制實(shí)際上是仿照J(rèn)ava中的SPI擴(kuò)展機(jī)制來(lái)實(shí)現(xiàn)的。

什么是 SPI 機(jī)制?

SPI 的全名為 Service Provider Interface.大多數(shù)開(kāi)發(fā)人員可能不熟悉,因?yàn)檫@個(gè)是針對(duì)廠商或者插件的。在java.util.ServiceLoader的文檔里有比較詳細(xì)的介紹。

簡(jiǎn)單的總結(jié)下 java SPI 機(jī)制的思想。我們系統(tǒng)里抽象的各個(gè)模塊,往往有很多不同的實(shí)現(xiàn)方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對(duì)象的設(shè)計(jì)里,我們一般推薦模塊之間基于接口編程,模塊之間不對(duì)實(shí)現(xiàn)類進(jìn)行硬編碼。一旦代碼里涉及具體的實(shí)現(xiàn)類,就違反了可拔插的原則,如果需要替換一種實(shí)現(xiàn),就需要修改代碼。為了實(shí)現(xiàn)在模塊裝配的時(shí)候能不在程序里動(dòng)態(tài)指明,這就需要一種服務(wù)發(fā)現(xiàn)機(jī)制。

java SPI 就是提供這樣的一個(gè)機(jī)制:為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制。有點(diǎn)類似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設(shè)計(jì)中這個(gè)機(jī)制尤其重要。

Spring Boot 中的 SPI 機(jī)制

在 Spring 中也有一種類似與 Java SPI 的加載機(jī)制。它在 resources/META-INF/spring.factories 文件中配置接口的實(shí)現(xiàn)類名稱,然后在程序中讀取這些配置文件并實(shí)例化。

在 Spring 中也有一種類似與 Java SPI 的加載機(jī)制。它在 resources/META-INF/spring.factories 文件中配置接口的實(shí)現(xiàn)類名稱,然后在程序中讀取這些配置文件并實(shí)例化。

這種自定義的SPI機(jī)制是 Spring Boot Starter 實(shí)現(xiàn)的基礎(chǔ)。

在這里插入圖片描述

Spring Factories 實(shí)現(xiàn)原理是什么?

spring-core 包里定義了 SpringFactoriesLoader 類,這個(gè)類實(shí)現(xiàn)了檢索 META-INF/spring.factories 文件,并獲取指定接口的配置的功能。在這個(gè)類中定義了兩個(gè)對(duì)外的方法:

loadFactories 根據(jù)接口類獲取其實(shí)現(xiàn)類的實(shí)例,這個(gè)方法返回的是對(duì)象列表。 loadFactoryNames 根據(jù)接口獲取其接口類的名稱,這個(gè)方法返回的是類名的列表。

上面的兩個(gè)方法的關(guān)鍵都是從指定的 ClassLoader 中獲取 spring.factories 文件,并解析得到類名列表,具體代碼如下

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
    private SpringFactoriesLoader() {}
    public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryClass, "'factoryClass' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
        }
        List<T> result = new ArrayList(factoryNames.size());
        Iterator var5 = factoryNames.iterator();
        while(var5.hasNext()) {
            String factoryName = (String)var5.next();
            result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
        }
        AnnotationAwareOrderComparator.sort(result);
        return result;
    }
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();
                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;
                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }
                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }
    private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
        try {
            Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
            if (!factoryClass.isAssignableFrom(instanceClass)) {
                throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
            } else {
                return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance();
            }
        } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);
        }
    }
}

從代碼中我們可以知道,在這個(gè)方法中會(huì)遍歷整個(gè) spring-boot 項(xiàng)目的 classpath 下 ClassLoader 中所有 jar 包下的 spring.factories文件。也就是說(shuō)我們可以在自己的 jar 中配置 spring.factories 文件,不會(huì)影響到其它地方的配置,也不會(huì)被別人的配置覆蓋。

Spring Factories 在 Spring Boot 中的應(yīng)用

在 Spring Boot 的很多包中都能夠找到 spring.factories 文件,接下來(lái)我們以 spring-boot-autoconfigure 包為例進(jìn)行介紹

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

結(jié)合前面的內(nèi)容,可以看出 spring.factories 文件可以將 spring-boot 項(xiàng)目包以外的 bean(即在 pom 文件中添加依賴中的 bean)注冊(cè)到 spring-boot 項(xiàng)目的 spring 容器。

由于@ComponentScan 注解只能掃描 spring-boot 項(xiàng)目包內(nèi)的 bean 并注冊(cè)到 spring 容器中,因此需要 @EnableAutoConfiguration 注解來(lái)注冊(cè)項(xiàng)目包外的bean。

而 spring.factories 文件,則是用來(lái)記錄項(xiàng)目包外需要注冊(cè)的bean類名。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java基礎(chǔ)類之ArrayUtils工具類詳解

    Java基礎(chǔ)類之ArrayUtils工具類詳解

    這篇文章主要介紹了java.ArrayDeque類使用方法,java.ArrayDeque類提供了可調(diào)整大小的陣列,并實(shí)現(xiàn)了Deque接口,感興趣的小伙伴們可以參考一下
    2021-09-09
  • SpringBoot整合Ureport2報(bào)表及常見(jiàn)使用方法

    SpringBoot整合Ureport2報(bào)表及常見(jiàn)使用方法

    這篇文章主要介紹了SpringBoot整合Ureport2報(bào)表及常見(jiàn)使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • idea中將單個(gè)java類導(dǎo)出為jar包文件的方法

    idea中將單個(gè)java類導(dǎo)出為jar包文件的方法

    這篇文章主要給大家介紹了關(guān)于idea中將單個(gè)java類導(dǎo)出為jar包文件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • java實(shí)現(xiàn)非法訪問(wèn)異常示例

    java實(shí)現(xiàn)非法訪問(wèn)異常示例

    創(chuàng)建ExceptionTest類,在該類的main()方法中,使用反射獲得String類的所有域,不要使用setAccessible方法修改這些域的可見(jiàn)性,然后通過(guò)反射獲得私有域中與”hash”相匹配的int值,并使用try-catch語(yǔ)句塊捕獲非法訪問(wèn)異常
    2014-02-02
  • java設(shè)計(jì)模式--橋接模式詳解

    java設(shè)計(jì)模式--橋接模式詳解

    這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之橋接模式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能給你帶來(lái)幫助
    2021-07-07
  • java日志門面之JCL和SLF4J詳解

    java日志門面之JCL和SLF4J詳解

    這篇文章主要給大家介紹了關(guān)于java日志門面之JCL和SLF4J的相關(guān)資料,在系統(tǒng)開(kāi)發(fā)過(guò)程中日志框架的選擇與更換是一大挑戰(zhàn),日志門面的概念,如JCL和SLF4J,允許開(kāi)發(fā)者面向接口編程,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • @Configuration與@Component作為配置類的區(qū)別詳解

    @Configuration與@Component作為配置類的區(qū)別詳解

    這篇文章主要介紹了@Configuration與@Component作為配置類的區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Java模擬微信來(lái)電提醒示例

    Java模擬微信來(lái)電提醒示例

    這篇文章主要為大家介紹了Java模擬微信來(lái)電提醒示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Java中的值傳遞以及引用傳遞和數(shù)組傳遞詳解

    Java中的值傳遞以及引用傳遞和數(shù)組傳遞詳解

    這篇文章主要介紹了Java中的值傳遞以及引用傳遞和數(shù)組傳遞詳解,Java不允許程序員選擇按值傳遞還是按引用傳遞各個(gè)參數(shù),就對(duì)象而言,不是將對(duì)象本身傳遞給方法,而是將對(duì)象的的引用或者說(shuō)對(duì)象的首地址傳遞給方法,引用本身是按值傳遞的,需要的朋友可以參考下
    2023-07-07
  • java.mail實(shí)現(xiàn)發(fā)送郵件

    java.mail實(shí)現(xiàn)發(fā)送郵件

    這篇文章主要為大家詳細(xì)介紹了java.mail實(shí)現(xiàn)發(fā)送郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02

最新評(píng)論