欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解讀spring.factories文件配置詳情

 更新時(shí)間:2025年03月22日 11:38:20   作者:曠野歷程  
這篇文章主要介紹了解讀spring.factories文件配置詳情,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

使用場(chǎng)景

在程序開(kāi)發(fā)中,可能會(huì)出現(xiàn)包名不一樣的情況(如:pom 依賴的很多的 jar),如何解決Spring Boot不能被默認(rèn)路徑掃描呢?

  • 方法一:在 Spring Boot Application 主類上使用 @Import 注解。
  • 方法二:使用 spring.factories 文件

方法一比較簡(jiǎn)單,在此就不做過(guò)多介紹,主要談?wù)?spring.factories 使用。

作用

spring.factories 的作用是使用外部 jar 時(shí)不用再寫配置,會(huì)自動(dòng)加入已經(jīng)配置好的配置。

內(nèi)部原理機(jī)制

spring.factories 這種機(jī)制實(shí)際上是仿照 java 中的 SPI 擴(kuò)展機(jī)制實(shí)現(xiàn)的。

SPI機(jī)制

SPI全稱Service Provider Interface,是 Java 提供的一套用來(lái)被第三方實(shí)現(xiàn)或者擴(kuò)展的接口,其意義在于為某個(gè)接口尋找服務(wù)的實(shí)現(xiàn),主要應(yīng)用在框架中用來(lái)尋找組件,提高擴(kuò)展性。

面向的對(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 機(jī)制:為某個(gè)接口尋找服務(wù)的實(shí)現(xiàn)的機(jī)制,有點(diǎn)類似 IOC 的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設(shè)計(jì)中這個(gè)機(jī)制很重要。
  • Spring Boot 中的 SPI 機(jī)制:在 META-INFO/spring.factories 文件中配置接口的實(shí)現(xiàn)類名稱,然后在程序中讀取這些配置文件并實(shí)例化。這種自定義的 SPI 機(jī)制就是 Spring Boot Starter 實(shí)現(xiàn)的基礎(chǔ)。

Spring Factories 實(shí)現(xiàn)原理

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> factoryType, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryType, "'factoryType' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
        }

        List<T> result = new ArrayList(factoryImplementationNames.size());
        Iterator var5 = factoryImplementationNames.iterator();

        while(var5.hasNext()) {
            String factoryImplementationName = (String)var5.next();
            result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
        }

        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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 factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.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 factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
        try {
            Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
            if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
                throw new IllegalArgumentException("Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
            } else {
                return ReflectionUtils.accessibleConstructor(factoryImplementationClass, new Class[0]).newInstance();
            }
        } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]", var4);
        }
    }
}

從上述代碼中可以看到,在這個(gè)方法中會(huì)遍歷整個(gè) ClassLoader 中所有 jar 包下的 spring.factories 文件,文件之間不會(huì)相互影響配置,也不回被別人的配置覆蓋。

spring.factories 的是通過(guò) Properties 解析得到的,所以在寫文件中的內(nèi)容都是按照下面這種方式配置的。

用法及配置

spring.factories 文件必須放在 resources 目錄下的 META-INF 的目錄下,否則不會(huì)生效。如果一個(gè)接口希望配置多個(gè)實(shí)現(xiàn)類,可以用","分割。

spring.factories 各種配置的具體含義

ApplicationContextInitializer

  • 該配置項(xiàng)用來(lái)配置實(shí)現(xiàn)了 ApplicationContextInitializer 接口的類,這些類用來(lái)實(shí)現(xiàn)上下文初始化。
  • 配置如下:
org.springframework.context.ApplicationContextInitializer=\
com.xh.config.MyApplicationContextInitializer
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
 
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("MyApplicationContextInitializer.initialize() " + applicationContext);
    }
 
}

ApplicationListener

  • 配置應(yīng)用程序監(jiān)聽(tīng)器,該監(jiān)聽(tīng)器必須實(shí)現(xiàn) ApplicationListener 接口。它可以用來(lái)監(jiān)聽(tīng) ApplicationEvent 事件。
  • 配置如下:
org.springframework.context.ApplicationListener=\
com.xh.factories.listener.EmailApplicationListener
@Slf4j
public class EmailApplicationListener implements ApplicationListener<EmailMessageEvent> {

    @Override
    public void onApplicationEvent(EmailMessageEvent event) {
        log.info("模擬發(fā)送郵件... ");
        log.info("EmailApplicationListener 接受到的消息:{}", event.getContent());
    }
}

AutoConfigurationImportListener

  • 該配置項(xiàng)用來(lái)配置自動(dòng)配置導(dǎo)入監(jiān)聽(tīng)器,監(jiān)聽(tīng)器必須實(shí)現(xiàn) AutoConfigurationImportListener 接口。
  • 該監(jiān)聽(tīng)器可以監(jiān)聽(tīng) AutoConfigurationImportEvent 事件。
  • 配置如下:
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
com.xh.config.MyAutoConfigurationImportListener
public class MyAutoConfigurationImportListener implements AutoConfigurationImportListener {
 
    @Override
    public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
        System.out.println("MyAutoConfigurationImportListener.onAutoConfigurationImportEvent() " + event);
    }
 
}

AutoConfigurationImportFilter

  • 配置自動(dòng)配置導(dǎo)入過(guò)濾器,過(guò)濾器必須實(shí)現(xiàn) AutoConfigurationImportFilter 接口。
  • 該過(guò)濾器用來(lái)過(guò)濾那些自動(dòng)配置類可用。
  • 配置如下:
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
com.xh.config.MyConfigurationCondition
public class MyConfigurationCondition implements AutoConfigurationImportFilter {
 
    @Override
    public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
        System.out.println("MyConfigurationCondition.match() autoConfigurationClasses=" +  Arrays.toString(autoConfigurationClasses) + ", autoConfigurationMetadata=" + autoConfigurationMetadata);
        return new boolean[0];
    }
 
}

EnableAutoConfiguration

  • 配置自動(dòng)配置類。這些配置類需要添加 @Configuration 注解。
  • 配置如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xh.config.MyConfiguration
@Configuration
public class MyConfiguration {
 
    public MyConfiguration() {
        System.out.println("MyConfiguration()");
    }
 
}

FailureAnalyzer

  • 配置自定的錯(cuò)誤分析類,該分析器需要實(shí)現(xiàn) FailureAnalyzer 接口。
  • 配置如下:
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.huangx.springboot.autoconfig.MyFailureAnalyzer
/**
 * 自定義錯(cuò)誤分析器
 */
public class MyFailureAnalyzer implements FailureAnalyzer {
 
    @Override
    public FailureAnalysis analyze(Throwable failure) {
        System.out.println("MyFailureAnalyzer.analyze() failure=" + failure);
        return new FailureAnalysis("MyFailureAnalyzer execute", "test spring.factories", failure);
    }
 
}

TemplateAvailabilityProvider

  • 配置模板的可用性提供者,提供者需要實(shí)現(xiàn) TemplateAvailabilityProvider 接口。
  • 配置如下:
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
com.huangx.springboot.autoconfig.MyTemplateAvailabilityProvider
/**
 * 驗(yàn)證指定的模板是否支持
 */
public class MyTemplateAvailabilityProvider implements TemplateAvailabilityProvider {
 
    @Override
    public boolean isTemplateAvailable(String view, Environment environment, ClassLoader classLoader, ResourceLoader resourceLoader) {
        System.out.println("MyTemplateAvailabilityProvider.isTemplateAvailable() view=" + view + ", environment=" + environment + ", classLoader=" + classLoader + "resourceLoader=" + resourceLoader);
        return false;
    }
 
}

總結(jié)

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

相關(guān)文章

  • SpringBoot 自動(dòng)裝配的原理詳解分析

    SpringBoot 自動(dòng)裝配的原理詳解分析

    這篇文章主要介紹了SpringBoot 自動(dòng)裝配的原理詳解分析,文章通過(guò)通過(guò)一個(gè)案例來(lái)看一下自動(dòng)裝配的效果展開(kāi)詳情,感興趣的小伙伴可以參考一下
    2022-08-08
  • JAVA中的函數(shù)式接口Function和BiFunction詳解

    JAVA中的函數(shù)式接口Function和BiFunction詳解

    這篇文章主要介紹了JAVA中的函數(shù)式接口Function和BiFunction詳解,JDK的函數(shù)式接口都加上了@FunctionalInterface注解進(jìn)行標(biāo)識(shí),但是無(wú)論是否加上該注解只要接口中只有一個(gè)抽象方法,都是函數(shù)式接口,需要的朋友可以參考下
    2024-01-01
  • Spring Boot 參數(shù)校驗(yàn)的具體實(shí)現(xiàn)方式

    Spring Boot 參數(shù)校驗(yàn)的具體實(shí)現(xiàn)方式

    這篇文章主要介紹了Spring Boot 參數(shù)校驗(yàn)的具體實(shí)現(xiàn)方式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • java通過(guò)Arrays.sort(int[] a)實(shí)現(xiàn)由大到小排序的方法實(shí)現(xiàn)

    java通過(guò)Arrays.sort(int[] a)實(shí)現(xiàn)由大到小排序的方法實(shí)現(xiàn)

    Java中的Arrays.sort()方法是一種內(nèi)置的排序方法,用于對(duì)數(shù)組進(jìn)行排序,本文就來(lái)介紹一下java中的Arrays.sort()排序方法的用法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • springcloud下hibernate本地化方言配置方式

    springcloud下hibernate本地化方言配置方式

    這篇文章主要介紹了springcloud下hibernate本地化方言配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Springboot 如何實(shí)現(xiàn)filter攔截token驗(yàn)證和跨域

    Springboot 如何實(shí)現(xiàn)filter攔截token驗(yàn)證和跨域

    這篇文章主要介紹了Springboot 如何實(shí)現(xiàn)filter攔截token驗(yàn)證和跨域操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java 添加Word目錄的2種方法示例代碼詳解

    Java 添加Word目錄的2種方法示例代碼詳解

    目錄是一種能夠快速、有效地幫助讀者了解文檔或書籍主要內(nèi)容的方式。這篇文章主要介紹了Java 添加Word目錄的2種方法 ,需要的朋友可以參考下
    2019-06-06
  • Java深入講解Object類常用方法的使用

    Java深入講解Object類常用方法的使用

    前面java繼承中說(shuō)到的Object類是java中一個(gè)特殊的類,所有的類都是直接或者間接的繼承自O(shè)bject類,即如果某個(gè)類沒(méi)有使用extends關(guān)鍵字則默認(rèn)是java.lang.Object類的子類,所以所有的類都可以使用Object類中定義的方法,下面介紹Object類的常用方法
    2022-04-04
  • Java多線程之原子類解析

    Java多線程之原子類解析

    這篇文章主要介紹了Java多線程之原子類解析,Java原子類是一種多線程編程中常用的工具,用于實(shí)現(xiàn)線程安全的操作,它們提供了一種原子性操作的機(jī)制,確保多個(gè)線程同時(shí)訪問(wèn)共享變量時(shí)的數(shù)據(jù)一致性,需要的朋友可以參考下
    2023-10-10
  • Maven之pom.xml文件中的Build配置解析

    Maven之pom.xml文件中的Build配置解析

    這篇文章主要介紹了Maven之pom.xml文件中的Build配置解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評(píng)論