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

java開發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用

 更新時(shí)間:2022年10月28日 14:31:52   作者:夢(mèng)想實(shí)現(xiàn)家_Z  
這篇文章主要為大家介紹了java開發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

做過java web開發(fā)的小伙伴大多數(shù)時(shí)候都需要鏈接數(shù)據(jù)庫,這個(gè)時(shí)候就需要配置數(shù)據(jù)庫引擎DriverClassName參數(shù),這樣我們的java應(yīng)用才能通過數(shù)據(jù)庫廠商給的Driver與指定的數(shù)據(jù)庫建立通信。但是這里就有一個(gè)疑問:

java.sql.Driver是jdk自帶的接口,它是由BoostrapClassLoader加載的,DriverClassName是外部廠商提供的具體實(shí)現(xiàn),是由AppClassLoader加載的,要建立與數(shù)據(jù)庫的通信,必然是通過java.sql.Driver接口方法發(fā)起的,那么在java.sql.Driver是如何拿到具體實(shí)現(xiàn)的呢?它是不是違背了ClassLoader的雙親委派模式呢?

如何繞過雙親委派模式

為了拿到AppClassLoader中加載的java.sql.Driver實(shí)現(xiàn)類,我們可以查看一下DriverManager是怎么處理的:

public static Driver getDriver(String url)
        throws SQLException {
    println("DriverManager.getDriver("" + url + "")");
    ensureDriversInitialized();
    ......
}
private static void ensureDriversInitialized() {
    ......
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    // 核心代碼ServiceLoader
                    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                    Iterator<Driver> driversIterator = loadedDrivers.iterator();
                    try {
                        while (driversIterator.hasNext()) {
                            driversIterator.next();
                        }
                    } catch (Throwable t) {
                        // Do nothing
                    }
                    return null;
                }
            });
    ......
}

我們最終可以發(fā)現(xiàn),DriverManager通過ServiceLoader.load(Driver.class)就拿到了我們配置的DriverClassName實(shí)現(xiàn)類。這就實(shí)現(xiàn)在DriverManager中拿到了外部提供的Driver實(shí)現(xiàn),繞過來雙親委派模式。

ServiceLoader實(shí)現(xiàn)機(jī)制

我們來看一下ServiceLoader是如何實(shí)現(xiàn)SPI機(jī)制的,先從ServiceLoader.load()方法入手:

    public static <S> ServiceLoader<S> load(Class<S> service) {
        // 從當(dāng)前線程中獲取ClassLoader
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        // 創(chuàng)建一個(gè)ServiceLoader對(duì)象
        return new ServiceLoader<>(Reflection.getCallerClass(), service, cl);
    }

1.從當(dāng)前線程中獲取ClassLoader;因?yàn)樵趧?chuàng)建AppClassLoader后,將AppClassLoader設(shè)置進(jìn)當(dāng)前線程的上下文中;

2.根據(jù)ClassLoader以及目標(biāo)接口類創(chuàng)建一個(gè)ServiceLoader對(duì)象;

其實(shí)ServiceLoader核心代碼在hasNext()方法中:

       @Override
        public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

最終都會(huì)調(diào)用到hasNextService()方法中:

private boolean hasNextService() {
    // nextProvider默認(rèn)為null,如果通過next()取出來了,nextProvider就會(huì)變成null
    while (nextProvider == null && nextError == null) {
        try {
            // 找到目標(biāo)實(shí)現(xiàn)類
            Class<?> clazz = nextProviderClass();
            if (clazz == null)
                return false;
            if (clazz.getModule().isNamed()) {
                // ignore class if in named module
                continue;
            }
            // 判斷service接口是否和clazz有父子關(guān)系
            if (service.isAssignableFrom(clazz)) {
                Class<? extends S> type = (Class<? extends S>) clazz;
                // 獲取無參構(gòu)造函數(shù)
                Constructor<? extends S> ctor
                            = (Constructor<? extends S>)getConstructor(clazz);
                // 包裝成一個(gè)ProviderImpl對(duì)象
                ProviderImpl<S> p = new ProviderImpl<S>(service, type, ctor, acc);
                // 并賦值給nextProvider
                nextProvider = (ProviderImpl<T>) p;
            } else {
                fail(service, clazz.getName() + " not a subtype");
            }
        } catch (ServiceConfigurationError e) {
            nextError = e;
        }
    }
    return true;
}

外部提供的實(shí)現(xiàn)類一定要有一個(gè)無參構(gòu)造函數(shù),否則會(huì)導(dǎo)致ServiceLoader加載失敗;

我們下面再繼續(xù)深入看看ServiceLoader是怎么找到實(shí)現(xiàn)類的:

private Class<?> nextProviderClass() {
    if (configs == null) {
        try {
            // 拼接文件名:"META-INF/services/接口名稱"
            // 比如接口名為:java.sql.Driver,
            // 那么文件路徑就是:"META-INF/services/java.sql.Driver"
            String fullName = PREFIX + service.getName();
            // 沒有指定ClassLoader,就通過getSystemClassLoader()加載目標(biāo)文件
            if (loader == null) {
                configs = ClassLoader.getSystemResources(fullName);
            } else if (loader == ClassLoaders.platformClassLoader()) {
                // 如果是platformClassLoader,它沒有class path,那么看看BootLoader有沒有class path
                if (BootLoader.hasClassPath()) {
                    configs = BootLoader.findResources(fullName);
                } else {
                    configs = Collections.emptyEnumeration();
                }
            } else {
                // 通過指定classLoader加載目標(biāo)文件
                configs = loader.getResources(fullName);
            }
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    // 上面代碼只會(huì)執(zhí)行一次,這樣configs就不會(huì)為null,下次進(jìn)來直接取下一個(gè)實(shí)現(xiàn)類
    // 把configs內(nèi)容解析成一個(gè)迭代器
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return null;
        }
        pending = parse(configs.nextElement());
    }
    // 通過迭代器獲取下一個(gè)實(shí)現(xiàn)類名稱
    String cn = pending.next();
    try {
        // 通過類名反射成Class對(duì)象
        return Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        fail(service, "Provider " + cn + " not found");
        return null;
    }
}

1.實(shí)現(xiàn)類的載入是因?yàn)樵?code>META-INF/services/文件夾中創(chuàng)建了以目標(biāo)接口名稱命名的文件,并在里面寫上了實(shí)現(xiàn)類的全路徑類名。

2.ServiceLoader通過ClassLoader從class path中載入目標(biāo)文件里面的內(nèi)容,并解析出實(shí)現(xiàn)類的全路徑類名;

3.最終通過反射的方式創(chuàng)建出實(shí)現(xiàn)類的Class對(duì)象,這樣就完成了SPI的實(shí)現(xiàn);

SPI在各個(gè)框架上的應(yīng)用

除了在數(shù)據(jù)庫Driver上使用了SPI,我們還可以發(fā)現(xiàn)SPI在各個(gè)框架上都有大量的應(yīng)用。比如我最近在看的Seata分布式事務(wù)框架,里面就有用到SPIio.seata.common.loader.EnhancedServiceLoader

另一個(gè)就是我們經(jīng)常使用的mysql-connector-java以及阿里的Druid:

小結(jié)

通過以上源碼分析以及示例演示,我們簡(jiǎn)單做一個(gè)小結(jié):

1.ServiceLoader打破雙親委派模式的方式通過獲取當(dāng)前線程上下文中的ClassLoader完成的;

2.SPI的實(shí)現(xiàn)類名稱必須放在META-INF/services/文件夾下面,以目標(biāo)接口名稱作為文件名稱,文件內(nèi)容為目標(biāo)實(shí)現(xiàn)類全路徑類名;

3.目標(biāo)實(shí)現(xiàn)類必須要有一個(gè)無參構(gòu)造函數(shù),否則SPI會(huì)失??;

以上就是java開發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用的詳細(xì)內(nèi)容,更多關(guān)于java開發(fā)ServiceLoader SPI的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot攔截器以及源碼詳析

    SpringBoot攔截器以及源碼詳析

    攔截器在我們平時(shí)的項(xiàng)目中用處有很多,如:日志記錄(我們后續(xù)章節(jié)會(huì)講到)、用戶登錄狀態(tài)攔截、安全攔截等等,所以下面這篇文章主要給大家介紹了關(guān)于SpringBoot攔截器以及源碼的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • java反射獲取一個(gè)object屬性值代碼解析

    java反射獲取一個(gè)object屬性值代碼解析

    這篇文章主要介紹了java反射獲取一個(gè)object屬性值代碼解析,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • Spring中XML schema擴(kuò)展機(jī)制的深入講解

    Spring中XML schema擴(kuò)展機(jī)制的深入講解

    這篇文章主要給大家介紹了關(guān)于Spring中XML schema擴(kuò)展機(jī)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • SpringBoot使用RestTemplate發(fā)送http請(qǐng)求的實(shí)操演示

    SpringBoot使用RestTemplate發(fā)送http請(qǐng)求的實(shí)操演示

    RestTemplate是Spring 框架提供的 ,可用于在應(yīng)用中調(diào)用 rest 服務(wù),它簡(jiǎn)化了與 http 服務(wù)的通信方式,統(tǒng)一了 RESTful 的標(biāo)準(zhǔn),封裝了 http 鏈接,本文給大家介紹了SpringBoot使用RestTemplate發(fā)送http請(qǐng)求的實(shí)操演示,需要的朋友可以參考下
    2024-11-11
  • Java并行執(zhí)行任務(wù)的幾種方案小結(jié)

    Java并行執(zhí)行任務(wù)的幾種方案小結(jié)

    這篇文章主要介紹了Java并行執(zhí)行任務(wù)的幾種方案小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • IDEA中java斷言assert語法及使用

    IDEA中java斷言assert語法及使用

    這篇文章主要介紹了IDEA中java斷言assert語法詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Spring Boot整合logback一個(gè)簡(jiǎn)單的日志集成架構(gòu)

    Spring Boot整合logback一個(gè)簡(jiǎn)單的日志集成架構(gòu)

    今天小編就為大家分享一篇關(guān)于Spring Boot整合logback一個(gè)簡(jiǎn)單的日志集成架構(gòu),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Java String類用法詳解

    Java String類用法詳解

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識(shí),文章圍繞著Java String類用法展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • MyBatis高級(jí)映射及延遲加載的實(shí)現(xiàn)

    MyBatis高級(jí)映射及延遲加載的實(shí)現(xiàn)

    MyBatis在處理對(duì)象關(guān)系映射時(shí),多對(duì)一關(guān)系是常見的場(chǎng)景,本文就來介紹了MyBatis高級(jí)映射及延遲加載的實(shí)現(xiàn),感興趣的可以了解一下
    2024-11-11
  • 深入理解Java設(shè)計(jì)模式之訪問者模式

    深入理解Java設(shè)計(jì)模式之訪問者模式

    這篇文章主要介紹了JAVA設(shè)計(jì)模式之訪問者模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解
    2021-11-11

最新評(píng)論

玉山县| 北碚区| 井陉县| 阳曲县| 秦安县| 昭平县| 孝义市| 城口县| 涿州市| 新密市| 抚松县| 禹城市| 牟定县| 云和县| 拉孜县| 克山县| 岑溪市| 甘谷县| 云林县| 阜阳市| 东明县| 钟祥市| 双牌县| 道孚县| 丰原市| 湛江市| 莒南县| 井冈山市| 泌阳县| 康定县| 都江堰市| 桐梓县| 台南市| 昌吉市| 北京市| 尉犁县| 永胜县| 彰化县| 永平县| 琼结县| 东山县|