最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring-boot中的SPI機(jī)制實(shí)例講解

 更新時(shí)間:2021年07月26日 09:47:49   作者:小manong  
這篇文章主要介紹了spring-boot中的SPI機(jī)制實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、從java類加載機(jī)制說(shuō)起

java中的類加載器負(fù)載加載來(lái)自文件系統(tǒng)、網(wǎng)絡(luò)或者其他來(lái)源的類文件。jvm的類加載器默認(rèn)使用的是雙親委派模式。三種默認(rèn)的類加載器Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader(Application ClassLoader)每一個(gè)中類加載器都確定了從哪一些位置加載文件。于此同時(shí)我們也可以通過(guò)繼承java.lang.classloader實(shí)現(xiàn)自己的類加載器。

Bootstrap ClassLoader:負(fù)責(zé)加載JDK自帶的rt.jar包中的類文件,是所有類加載的父類

Extension ClassLoader:負(fù)責(zé)加載java的擴(kuò)展類庫(kù)從jre/lib/ect目錄或者java.ext.dirs系統(tǒng)屬性指定的目錄下加載類,是System ClassLoader的父類加載器

System ClassLoader:負(fù)責(zé)從classpath環(huán)境變量中加載類文件

1、雙親委派模型

原理:當(dāng)一個(gè)類加載器收到類加載任務(wù)時(shí),會(huì)先交給自己的父加載器去完成,因此最終加載任務(wù)都會(huì)傳遞到最頂層的BootstrapClassLoader,只有當(dāng)父加載器無(wú)法完成加載任務(wù)時(shí),才會(huì)嘗試自己來(lái)加載。

具體:根據(jù)雙親委派模式,在加載類文件的時(shí)候,子類加載器首先將加載請(qǐng)求委托給它的父加載器,父加載器會(huì)檢測(cè)自己是否已經(jīng)加載過(guò)類,如果已經(jīng)加載則加載過(guò)程結(jié)束,如果沒(méi)有加載的話則請(qǐng)求繼續(xù)向上傳遞直Bootstrap ClassLoader。

如果請(qǐng)求向上委托過(guò)程中,如果始終沒(méi)有檢測(cè)到該類已經(jīng)加載,則Bootstrap ClassLoader開(kāi)始嘗試從其對(duì)應(yīng)路勁中加載該類文件,如果失敗則由子類加載器繼續(xù)嘗試加載,直至發(fā)起加載請(qǐng)求的子加載器為止。

采用雙親委派模式可以保證類型加載的安全性,不管是哪個(gè)加載器加載這個(gè)類,最終都是委托給頂層的BootstrapClassLoader來(lái)加載的,只有父類無(wú)法加載自己猜嘗試加載,這樣就可以保證任何的類加載器最終得到的都是同樣一個(gè)Object對(duì)象。

protected Class<?> loadClass(String name, boolean resolve) {
    synchronized (getClassLoadingLock(name)) {
    // 首先,檢查該類是否已經(jīng)被加載,如果從JVM緩存中找到該類,則直接返回
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            // 遵循雙親委派的模型,首先會(huì)通過(guò)遞歸從父加載器開(kāi)始找,
            // 直到父類加載器是BootstrapClassLoader為止
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {}
        if (c == null) {
            // 如果還找不到,嘗試通過(guò)findClass方法去尋找
            // findClass是留給開(kāi)發(fā)者自己實(shí)現(xiàn)的,也就是說(shuō)
            // 自定義類加載器時(shí),重寫(xiě)此方法即可
           c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
    }
}

2.雙親委派模型缺陷

在雙親委派模型中,子類加載器可以使用父類加載器已經(jīng)加載的類,而父類加載器無(wú)法使用子類加載器已經(jīng)加載的。這就導(dǎo)致了雙親委派模型并不能解決所有的類加載器問(wèn)題。

案例:Java 提供了很多服務(wù)提供者接口(Service Provider Interface,SPI),允許第三方為這些接口提供實(shí)現(xiàn)。

常見(jiàn)的 SPI 有 JDBC、JNDI、JAXP 等,這些SPI的接口由核心類庫(kù)提供,卻由第三方實(shí)現(xiàn),這樣就存在一個(gè)問(wèn)題:SPI 的接口是 Java 核心庫(kù)的一部分,是由BootstrapClassLoader加載的;SPI實(shí)現(xiàn)的Java類一般是由AppClassLoader來(lái)加載的。

BootstrapClassLoader是無(wú)法找到 SPI 的實(shí)現(xiàn)類的,因?yàn)樗患虞dJava的核心庫(kù)。它也不能代理給AppClassLoader,因?yàn)樗亲铐攲拥念惣虞d器。也就是說(shuō),雙親委派模型并不能解決這個(gè)問(wèn)題

3.使用線程上下文類加載器(ContextClassLoader)加載

如果不做任何的設(shè)置,Java應(yīng)用的線程的上下文類加載器默認(rèn)就是AppClassLoader。在核心類庫(kù)使用SPI接口時(shí),傳遞的類加載器使用線程上下文類加載器,就可以成功的加載到SPI實(shí)現(xiàn)的類。線程上下文類加載器在很多SPI的實(shí)現(xiàn)中都會(huì)用到。

通常我們可以通過(guò)Thread.currentThread().getClassLoader()和Thread.currentThread().getContextClassLoader()獲取線程上下文類加載器。

4、使用類加載器加載資源文件,比如jar包

類加載器除了加載class外,還有一個(gè)非常重要功能,就是加載資源,它可以從jar包中讀取任何資源文件,比如,ClassLoader.getResources(String name)方法就是用于讀取jar包中的資源文件

//獲取資源的方法
public Enumeration<URL> getResources(String name) throws IOException {
    Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
    if (parent != null) {
        tmp[0] = parent.getResources(name);
    } else {
        tmp[0] = getBootstrapResources(name);
    }
    tmp[1] = findResources(name);
    return new CompoundEnumeration<>(tmp);
}

它的邏輯其實(shí)跟類加載的邏輯是一樣的,首先判斷父類加載器是否為空,不為空則委托父類加載器執(zhí)行資源查找任務(wù),直到BootstrapClassLoader,最后才輪到自己查找。

而不同的類加載器負(fù)責(zé)掃描不同路徑下的jar包,就如同加載class一樣,最后會(huì)掃描所有的jar包,找到符合條件的資源文件。

// 使用線程上下文類加載器加載資源
public static void main(String[] args) throws Exception{
    // Array.class的完整路徑
    String name = "java/sql/Array.class";
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(name);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        System.out.println(url.toString());
    }
}

二、spring中SPI機(jī)制實(shí)現(xiàn)

1.SPI機(jī)制

(1)SPI思想

SPI的全名為Service Provider Interface.這個(gè)是針對(duì)廠商或者插件的。

SPI的思想:系統(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ī)制

(2)SPI約定

當(dāng)服務(wù)的提供者,提供了服務(wù)接口的一種實(shí)現(xiàn)之后,在jar包的META-INF/services/目錄里同時(shí)創(chuàng)建一個(gè)以服務(wù)接口命名的文件。該文件里就是實(shí)現(xiàn)該服務(wù)接口的具體實(shí)現(xiàn)類。而當(dāng)外部程序裝配這個(gè)模塊的時(shí)候,就能通過(guò)該jar包META-INF/services/里的配置文件找到具體的實(shí)現(xiàn)類名,并裝載實(shí)例化,完成模塊的注入。通過(guò)這個(gè)約定,就不需要把服務(wù)放在代碼中了,通過(guò)模塊被裝配的時(shí)候就可以發(fā)現(xiàn)服務(wù)類了。

2、SPI使用案例

common-logging apache最早提供的日志的門(mén)面接口。

只有接口,沒(méi)有實(shí)現(xiàn)。具體方案由各提供商實(shí)現(xiàn), 發(fā)現(xiàn)日志提供商是通過(guò)掃描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通過(guò)讀取該文件的內(nèi)容找到日志提工商實(shí)現(xiàn)類。

只要我們的日志實(shí)現(xiàn)里包含了這個(gè)文件,并在文件里制定 LogFactory工廠接口的實(shí)現(xiàn)類即可。

3、springboot中的類SPI擴(kuò)展機(jī)制

在springboot的自動(dòng)裝配過(guò)程中,最終會(huì)加載META-INF/spring.factories文件,而加載的過(guò)程是由SpringFactoriesLoader加載的。

從CLASSPATH下的每個(gè)Jar包中搜尋所有META-INF/spring.factories配置文件,然后將解析properties文件,找到指定名稱的配置后返回。

需要注意的是,其實(shí)這里不僅僅是會(huì)去ClassPath路徑下查找,會(huì)掃描所有路徑下的Jar包,只不過(guò)這個(gè)文件只會(huì)在Classpath下的jar包中。

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// spring.factories文件的格式為:key=value1,value2,value3
// 從所有的jar包中找到META-INF/spring.factories文件
// 然后從文件中解析出key=factoryClass類名稱的所有value值
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    // 取得資源文件的URL
    Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    List<String> result = new ArrayList<String>();
    // 遍歷所有的URL
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        // 根據(jù)資源文件URL解析properties文件,得到對(duì)應(yīng)的一組@Configuration類
        Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
        String factoryClassNames = properties.getProperty(factoryClassName);
        // 組裝數(shù)據(jù),并返回
        result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    }
    return result;
}

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

相關(guān)文章

  • JAVA中通過(guò)Hibernate-Validation進(jìn)行參數(shù)驗(yàn)證

    JAVA中通過(guò)Hibernate-Validation進(jìn)行參數(shù)驗(yàn)證

    這篇文章主要介紹了JAVA中通過(guò)Hibernate-Validation進(jìn)行參數(shù)驗(yàn)證,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 利用idea快速搭建一個(gè)spring-cloud(圖文)

    利用idea快速搭建一個(gè)spring-cloud(圖文)

    本文主要介紹了idea快速搭建一個(gè)spring-cloud,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Windows中Tomcat整合到Eclipse的圖文教程

    Windows中Tomcat整合到Eclipse的圖文教程

    下面小編就為大家?guī)?lái)一篇Windows中Tomcat整合到Eclipse的圖文教程。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • 使用SpringBoot實(shí)現(xiàn)微服務(wù)超時(shí)重試模式的示例

    使用SpringBoot實(shí)現(xiàn)微服務(wù)超時(shí)重試模式的示例

    這篇文章主要介紹了使用SpringBoot實(shí)現(xiàn)微服務(wù)超時(shí)重試模式的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下
    2020-11-11
  • springboot過(guò)濾器和攔截器的實(shí)例代碼

    springboot過(guò)濾器和攔截器的實(shí)例代碼

    這篇文章主要介紹了springboot過(guò)濾器和攔截器的實(shí)例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • SpringBoot3集成RocketMq場(chǎng)景分析

    SpringBoot3集成RocketMq場(chǎng)景分析

    RocketMQ因其架構(gòu)簡(jiǎn)單、業(yè)務(wù)功能豐富、具備極強(qiáng)可擴(kuò)展性等特點(diǎn)被廣泛應(yīng)用,比如金融業(yè)務(wù)、互聯(lián)網(wǎng)、大數(shù)據(jù)、物聯(lián)網(wǎng)等領(lǐng)域的業(yè)務(wù)場(chǎng)景,這篇文章主要介紹了SpringBoot3集成RocketMq,需要的朋友可以參考下
    2023-08-08
  • idea中增強(qiáng)for循環(huán)提示unexpected token問(wèn)題

    idea中增強(qiáng)for循環(huán)提示unexpected token問(wèn)題

    這篇文章主要介紹了idea中增強(qiáng)for循環(huán)提示unexpected token問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • HttpClient實(shí)現(xiàn)表單提交上傳文件

    HttpClient實(shí)現(xiàn)表單提交上傳文件

    這篇文章主要為大家詳細(xì)介紹了HttpClient實(shí)現(xiàn)表單提交上傳文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 詳解Java注解實(shí)現(xiàn)自己的ORM

    詳解Java注解實(shí)現(xiàn)自己的ORM

    這篇文章主要介紹了Java注解實(shí)現(xiàn)自己的ORM知識(shí),本文通過(guò)示例代碼給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2022-10-10
  • 使用java?-jar命令啟動(dòng)Spring?Boot應(yīng)用時(shí)指定特定配置文件的幾種實(shí)現(xiàn)方式

    使用java?-jar命令啟動(dòng)Spring?Boot應(yīng)用時(shí)指定特定配置文件的幾種實(shí)現(xiàn)方式

    這篇文章主要介紹了在使用java-jar命令啟動(dòng)SpringBoot應(yīng)用時(shí),指定特定配置文件的幾種方式,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2025-01-01

最新評(píng)論

磐石市| 肥西县| 兴国县| 南涧| 玉龙| 德清县| 平远县| 台南市| 台安县| 饶阳县| 临海市| 张北县| 左权县| 和龙市| 扬州市| 忻州市| 银川市| 彩票| 东港市| 九龙城区| 澜沧| 金阳县| 江陵县| 乌兰浩特市| 宁化县| 突泉县| 正宁县| 门头沟区| 聂荣县| 芦溪县| 财经| 宣威市| 嵊泗县| 赫章县| 南安市| 伊宁县| 嵊州市| 武山县| 象州县| 河东区| 东山县|