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

關(guān)于springboot中的SPI機制

 更新時間:2022年01月28日 11:00:35   作者:小manong  
這篇文章主要介紹了springboot中的SPI機制,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

一、從java類加載機制說起

java中的類加載器負載加載來自文件系統(tǒng)、網(wǎng)絡或者其他來源的類文件。jvm的類加載器默認使用的是雙親委派模式。

三種默認的類加載器Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader(Application ClassLoader)每一個中類加載器都確定了從哪一些位置加載文件。于此同時我們也可以通過繼承java.lang.classloader實現(xiàn)自己的類加載器。

  • Bootstrap ClassLoader:負責加載JDK自帶的rt.jar包中的類文件,是所有類加載的父類
  • Extension ClassLoader:負責加載java的擴展類庫從jre/lib/ect目錄或者java.ext.dirs系統(tǒng)屬性指定的目錄下加載類,是System ClassLoader的父類加載器
  • System ClassLoader:負責從classpath環(huán)境變量中加載類文件

1、雙親委派模型

原理:

當一個類加載器收到類加載任務時,會先交給自己的父加載器去完成,因此最終加載任務都會傳遞到最頂層的BootstrapClassLoader,只有當父加載器無法完成加載任務時,才會嘗試自己來加載。

具體:

根據(jù)雙親委派模式,在加載類文件的時候,子類加載器首先將加載請求委托給它的父加載器,父加載器會檢測自己是否已經(jīng)加載過類,如果已經(jīng)加載則加載過程結(jié)束,如果沒有加載的話則請求繼續(xù)向上傳遞直Bootstrap ClassLoader。如果請求向上委托過程中,如果始終沒有檢測到該類已經(jīng)加載,則Bootstrap ClassLoader開始嘗試從其對應路勁中加載該類文件,如果失敗則由子類加載器繼續(xù)嘗試加載,直至發(fā)起加載請求的子加載器為止。

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

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

2、雙親委派模型缺陷

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

案例:

Java 提供了很多服務提供者接口(Service Provider Interface,SPI),允許第三方為這些接口提供實現(xiàn)。常見的 SPI 有 JDBC、JNDI、JAXP 等,這些SPI的接口由核心類庫提供,卻由第三方實現(xiàn),這樣就存在一個問題:SPI 的接口是 Java 核心庫的一部分,是由BootstrapClassLoader加載的;SPI實現(xiàn)的Java類一般是由AppClassLoader來加載的。BootstrapClassLoader是無法找到 SPI 的實現(xiàn)類的,因為它只加載Java的核心庫。它也不能代理給AppClassLoader,因為它是最頂層的類加載器。也就是說,雙親委派模型并不能解決這個問題

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

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

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

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

類加載器除了加載class外,還有一個非常重要功能,就是加載資源,它可以從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);
}

它的邏輯其實跟類加載的邏輯是一樣的,首先判斷父類加載器是否為空,不為空則委托父類加載器執(zhí)行資源查找任務,直到BootstrapClassLoader,最后才輪到自己查找。而不同的類加載器負責掃描不同路徑下的jar包,就如同加載class一樣,最后會掃描所有的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機制實現(xiàn)

1、SPI機制

(1)SPI思想

SPI的全名為Service Provider Interface.這個是針對廠商或者插件的。

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

(2)SPI約定

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

2、SPI使用案例

common-logging apache最早提供的日志的門面接口。只有接口,沒有實現(xiàn)。具體方案由各提供商實現(xiàn), 發(fā)現(xiàn)日志提供商是通過掃描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通過讀取該文件的內(nèi)容找到日志提工商實現(xiàn)類。只要我們的日志實現(xiàn)里包含了這個文件,并在文件里制定 LogFactory工廠接口的實現(xiàn)類即可。

3、springboot中的類SPI擴展機制

在springboot的自動裝配過程中,最終會加載META-INF/spring.factories文件,而加載的過程是由SpringFactoriesLoader加載的。從CLASSPATH下的每個Jar包中搜尋所有META-INF/spring.factories配置文件,然后將解析properties文件,找到指定名稱的配置后返回。

需要注意的是,其實這里不僅僅是會去ClassPath路徑下查找,會掃描所有路徑下的Jar包,只不過這個文件只會在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文件,得到對應的一組@Configuration類
? ? ? ? Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
? ? ? ? String factoryClassNames = properties.getProperty(factoryClassName);
? ? ? ? // 組裝數(shù)據(jù),并返回
? ? ? ? result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
? ? }
? ? return result;
}

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

相關(guān)文章

  • java多線程-同步塊實例講解

    java多線程-同步塊實例講解

    本文主要介紹java多線程-同步塊的知識,這里整理了相關(guān)的詳細資料及簡單示例代碼,有興趣的小伙伴可以參考下
    2016-09-09
  • Sentinel中三種流控模式的使用詳解

    Sentinel中三種流控模式的使用詳解

    這篇文章主要為大家詳細介紹了Sentinel中三種流控模式(預熱模式,排隊等待模式和熱點規(guī)則)的使用,文中的示例代碼講解詳細,感興趣的可以了解下
    2023-08-08
  • SpringBoot2.0整合WebSocket代碼實例

    SpringBoot2.0整合WebSocket代碼實例

    這篇文章主要介紹了SpringBoot2.0整合WebSocket代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • Java取整與四舍五入

    Java取整與四舍五入

    本文詳細講解了Java取整與四舍五入,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-12-12
  • java中StringBuffer的length()和capacity()方法對比

    java中StringBuffer的length()和capacity()方法對比

    這篇文章主要介紹了java中StringBuffer的length()和capacity()方法對比,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • IDEA提示:Boolean method ‘xxx‘ is always inverted問題

    IDEA提示:Boolean method ‘xxx‘ is always&nb

    這篇文章主要介紹了IDEA提示:Boolean method ‘xxx‘ is always inverted問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java中File的實例詳解

    Java中File的實例詳解

    這篇文章主要介紹了Java中File的實例詳解的相關(guān)資料,File代表文件或者目錄的類,這里對使用方法進行詳細介紹,需要的朋友可以參考下
    2017-08-08
  • Java并發(fā)統(tǒng)計變量值偏差原因及解決方案

    Java并發(fā)統(tǒng)計變量值偏差原因及解決方案

    這篇文章主要介紹了Java并發(fā)統(tǒng)計變量值偏差原因及解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • gateway和jwt網(wǎng)關(guān)認證實現(xiàn)過程解析

    gateway和jwt網(wǎng)關(guān)認證實現(xiàn)過程解析

    這篇文章主要介紹了gateway和jwt網(wǎng)關(guān)認證實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • Java Semaphore實現(xiàn)高并發(fā)場景下的流量控制

    Java Semaphore實現(xiàn)高并發(fā)場景下的流量控制

    在java開發(fā)的工作中是否會出現(xiàn)這樣的場景,你需要實現(xiàn)一些異步運行的任務,該任務可能存在消耗大量內(nèi)存的情況,所以需要對任務進行并發(fā)控制。本文將介紹通過Semaphore類優(yōu)雅的實現(xiàn)并發(fā)控制,感興趣的可以了解一下
    2021-12-12

最新評論

隆安县| 沈阳市| 西乡县| 朝阳县| 柘城县| 明光市| 渑池县| 色达县| 加查县| 哈尔滨市| 察隅县| 清河县| 紫阳县| 神池县| 定兴县| 屏山县| 肥西县| 珲春市| 南汇区| 视频| 梧州市| 启东市| 大余县| 东台市| 怀柔区| 英德市| 镇远县| 台安县| 邹城市| 正宁县| 阿拉善右旗| 吴堡县| 湟源县| 海原县| 乌恰县| 尼玛县| 城市| 轮台县| 介休市| 芷江| 卓尼县|