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

詳解Java實現(xiàn)簡單SPI流程

 更新時間:2023年03月02日 10:29:54   作者:Redick01  
這篇文章主要介紹了Java實現(xiàn)簡單SPI流程,SPI英文全稱為Service Provider Interface,顧名思義,服務(wù)提供者接口,它是jdk提供給“服務(wù)提供廠商”或者“插件開發(fā)者”使用的接口

參考dubboshenyu網(wǎng)關(guān)實現(xiàn)自定義的SPI

SPI標(biāo)注注解

標(biāo)注提供SPI能力接口的注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SPI {
    /**
     * value
     * @return value
     */
    String value() default "";
}

標(biāo)準(zhǔn)SPI實現(xiàn)的注解@Join

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Join {
}

SPI核心實現(xiàn)

SPI的一些Class和擴(kuò)展對象緩存

SPI實現(xiàn)是一個懶加載的過程,只有當(dāng)通過get方法獲取擴(kuò)展的實例時才會加載擴(kuò)展,并創(chuàng)建擴(kuò)展實例,這里我們定義一個集合用于緩存擴(kuò)展類,擴(kuò)展對象等,代碼如下:

@Slf4j
@SuppressWarnings("all")
public class ExtensionLoader<T> {
    /**
     * SPI配置擴(kuò)展的文件位置
     * 擴(kuò)展文件命名格式為 SPI接口的全路徑名,如:com.redick.spi.test.TestSPI
     */
    private static final String DEFAULT_DIRECTORY = "META-INF/log-helper/";
    /**
     * 擴(kuò)展接口 {@link Class}
     */
    private final Class<T> tClass;
    /**
     * 擴(kuò)展接口 和 擴(kuò)展加載器 {@link ExtensionLoader} 的緩存
     */
    private static final Map<Class<?>, ExtensionLoader<?>> MAP = new ConcurrentHashMap<>();
    /**
     * 保存 "擴(kuò)展" 實現(xiàn)的 {@link Class}
     */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
    /**
     * "擴(kuò)展名" 對應(yīng)的 保存擴(kuò)展對象的Holder的緩存
     */
    private final Map<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    /**
     * 擴(kuò)展class 和 擴(kuò)展點的實現(xiàn)對象的緩存
     */
    private final Map<Class<?>, Object> joinInstances = new ConcurrentHashMap<>();
    /**
     * 擴(kuò)展點默認(rèn)的 "名稱" 緩存
     */
    private String cacheDefaultName;
    // 省略代碼后面介紹
}

獲取擴(kuò)展器ExtensionLoader

    public static<T> ExtensionLoader<T> getExtensionLoader(final Class<T> tClass) {
        // 參數(shù)非空校驗
        if (null == tClass) {
            throw new NullPointerException("tClass is null !");
        }
        // 參數(shù)應(yīng)該是接口
        if (!tClass.isInterface()) {
            throw new IllegalArgumentException("tClass :" + tClass + " is not interface !");
        }
        // 參數(shù)要包含@SPI注解
        if (!tClass.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("tClass " + tClass + "without @" + SPI.class + " Annotation !");
        }
        // 從緩存中獲取擴(kuò)展加載器,如果存在直接返回,如果不存在就創(chuàng)建一個擴(kuò)展加載器并放到緩存中
        ExtensionLoader<T> extensionLoader = (ExtensionLoader<T>) MAP.get(tClass);
        if (null != extensionLoader) {
            return extensionLoader;
        }
        MAP.putIfAbsent(tClass, new ExtensionLoader<>(tClass));
        return (ExtensionLoader<T>) MAP.get(tClass);
    }

擴(kuò)展加載器構(gòu)造方法

    public ExtensionLoader(final Class<T> tClass) {
        this.tClass = tClass;
    }

獲取SPI擴(kuò)展對象

獲取SPI擴(kuò)展對象是懶加載過程,第一次去獲取的時候是沒有的,會觸發(fā)從問家中加載資源,通過反射創(chuàng)建對象,并緩存起來。

    public T getJoin(String cacheDefaultName) {
        // 擴(kuò)展名 文件中的key
        if (StringUtils.isBlank(cacheDefaultName)) {
            throw new IllegalArgumentException("join name is null");
        }
        // 擴(kuò)展對象存儲緩存
        Holder<Object> objectHolder = cachedInstances.get(cacheDefaultName);
        // 如果擴(kuò)展對象的存儲是空的,創(chuàng)建一個擴(kuò)展對象存儲并緩存
        if (null == objectHolder) {
            cachedInstances.putIfAbsent(cacheDefaultName, new Holder<>());
            objectHolder = cachedInstances.get(cacheDefaultName);
        }
        // 從擴(kuò)展對象的存儲中獲取擴(kuò)展對象
        Object value = objectHolder.getT();
        // 如果對象是空的,就觸發(fā)創(chuàng)建擴(kuò)展,否則直接返回擴(kuò)展對象
        if (null == value) {
            synchronized (cacheDefaultName) {
                value = objectHolder.getT();
                if (null == value) {
                    // 創(chuàng)建擴(kuò)展對象
                    value = createExtension(cacheDefaultName);
                    objectHolder.setT(value);
                }
            }
        }
        return (T) value;
    }

創(chuàng)建擴(kuò)展對象

反射方式創(chuàng)建擴(kuò)展對象的實例

    private Object createExtension(String cacheDefaultName) {
        // 根據(jù)擴(kuò)展名字獲取擴(kuò)展的Class,從Holder中獲取 key-value緩存,然后根據(jù)名字從Map中獲取擴(kuò)展實現(xiàn)Class
        Class<?> aClass = getExtensionClasses().get(cacheDefaultName);
        if (null == aClass) {
            throw new IllegalArgumentException("extension class is null");
        }
        Object o = joinInstances.get(aClass);
        if (null == o) {
            try {
                // 創(chuàng)建擴(kuò)展對象并放到緩存中
                joinInstances.putIfAbsent(aClass, aClass.newInstance());
                o = joinInstances.get(aClass);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return o;
    }

從Holder中獲取獲取擴(kuò)展實現(xiàn)的Class集合

    public Map<String, Class<?>> getExtensionClasses() {
        // 擴(kuò)區(qū)SPI擴(kuò)展實現(xiàn)的緩存,對應(yīng)的就是擴(kuò)展文件中的 key - value
        Map<String, Class<?>> classes = cachedClasses.getT();
        if (null == classes) {
            synchronized (cachedClasses) {
                classes = cachedClasses.getT();
                if (null == classes) {
                    // 加載擴(kuò)展
                    classes = loadExtensionClass();
                    // 緩存擴(kuò)展實現(xiàn)集合
                    cachedClasses.setT(classes);
                }
            }
        }
        return classes;
    }

加載擴(kuò)展實現(xiàn)Class

加載擴(kuò)展實現(xiàn)Class,就是從文件中獲取擴(kuò)展實現(xiàn)的Class,然后緩存起來

    public Map<String, Class<?>> loadExtensionClass() {
        // 擴(kuò)展接口tClass,必須包含SPI注解
        SPI annotation = tClass.getAnnotation(SPI.class);
        if (null != annotation) {
            String v = annotation.value();
            if (StringUtils.isNotBlank(v)) {
                // 如果有默認(rèn)的擴(kuò)展實現(xiàn)名,用默認(rèn)的
                cacheDefaultName = v;
            }
        }
        Map<String, Class<?>> classes = new HashMap<>(16);
        // 從文件加載
        loadDirectory(classes);
        return classes;
    }
    private void loadDirectory(final Map<String, Class<?>> classes) {
        // 文件名
        String fileName = DEFAULT_DIRECTORY + tClass.getName();
        try {
            ClassLoader classLoader = ExtensionLoader.class.getClassLoader();
            // 讀取配置文件
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources(fileName)
                    : ClassLoader.getSystemResources(fileName);
            if (urls != null) {
                // 獲取所有的配置文件
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    // 加載資源
                    loadResources(classes, url);
                }
            }
        } catch (IOException e) {
            log.error("load directory error {}", fileName, e);
        }
    }
    private void loadResources(Map<String, Class<?>> classes, URL url) {
        // 讀取文件到Properties,遍歷Properties,得到配置文件key(名字)和value(擴(kuò)展實現(xiàn)的Class)
        try (InputStream inputStream = url.openStream()) {
            Properties properties = new Properties();
            properties.load(inputStream);
            properties.forEach((k, v) -> {
                // 擴(kuò)展實現(xiàn)的名字
                String name = (String) k;
                // 擴(kuò)展實現(xiàn)的Class的全路徑
                String classPath = (String) v;
                if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(classPath)) {
                    try {
                        // 加載擴(kuò)展實現(xiàn)Class,就是想其緩存起來,緩存到集合中
                        loadClass(classes, name, classPath);
                    } catch (ClassNotFoundException e) {
                        log.error("load class not found", e);
                    }
                }
            });
        } catch (IOException e) {
            log.error("load resouces error", e);
        }
    }
    private void loadClass(Map<String, Class<?>> classes, String name, String classPath) throws ClassNotFoundException {
        // 反射創(chuàng)建擴(kuò)展實現(xiàn)的Class
        Class<?> subClass = Class.forName(classPath);
        // 擴(kuò)展實現(xiàn)的Class要是擴(kuò)展接口的實現(xiàn)類
        if (!tClass.isAssignableFrom(subClass)) {
            throw new IllegalArgumentException("load extension class error " + subClass + " not sub type of " + tClass);
        }
        // 擴(kuò)展實現(xiàn)要有Join注解
        Join annotation = subClass.getAnnotation(Join.class);
        if (null == annotation) {
            throw new IllegalArgumentException("load extension class error " + subClass + " without @Join" +
                    "Annotation");
        }
        // 緩存擴(kuò)展實現(xiàn)Class
        Class<?> oldClass = classes.get(name);
        if (oldClass == null) {
            classes.put(name, subClass);
        } else if (oldClass != subClass) {
            log.error("load extension class error, Duplicate class oldClass is " + oldClass + "subClass is" + subClass);
        }
    }

存儲Holder

    public static class Holder<T> {
        private volatile T t;
        public T getT() {
            return t;
        }
        public void setT(T t) {
            this.t = t;
        }
    }

測試SPI

定義SPI接口

@SPI
public interface TestSPI {
    void test();
}

擴(kuò)展實現(xiàn)1和2

@Join
public class TestSPI1Impl implements TestSPI {
    @Override
    public void test() {
        System.out.println("test1");
    }
}
@Join
public class TestSPI2Impl implements TestSPI {
    @Override
    public void test() {
        System.out.println("test2");
    }
}

在resources文件夾下創(chuàng)建META-INF/log-helper文件夾,并創(chuàng)建擴(kuò)展文件

文件名稱(接口全路徑名):com.redick.spi.test.TestSPI

文件內(nèi)容

testSPI1=com.redick.spi.test.TestSPI1Impl
testSPI2=com.redick.spi.test.TestSPI2Impl

動態(tài)使用測試

public class SpiExtensionFactoryTest {
    @Test
    public void getExtensionTest() {
        TestSPI testSPI = ExtensionLoader.getExtensionLoader(TestSPI.class).getJoin("testSPI1");
        testSPI.test();
    }
}

測試結(jié)果:

test1

public class SpiExtensionFactoryTest {
    @Test
    public void getExtensionTest() {
        TestSPI testSPI = ExtensionLoader.getExtensionLoader(TestSPI.class).getJoin("testSPI2");
        testSPI.test();
    }
}

測試結(jié)果:

test2

總結(jié)

實現(xiàn)一個自定義的SPI機(jī)制其核心的邏輯就是擴(kuò)展的加載,本篇是參考Dubbo等開源項目簡單實現(xiàn)了一個SPI機(jī)制的核心代碼,核心邏輯就是從SPI擴(kuò)展的配置文件中加載擴(kuò)展實現(xiàn)的流程,通常情況下,SPI的應(yīng)用場景出現(xiàn)在高度可擴(kuò)展組件,并且在使用過程中有需求能夠靈活切換不同的實現(xiàn)的時候。比如程序使用限流組件,使用“令牌桶算法”和“漏桶算法”分別實現(xiàn)了限流邏輯,在業(yè)務(wù)使用限流算法的過程中,就可以通過SPI機(jī)制在程序啟動過程中將兩種算法實現(xiàn)的組件加載好,然后通過參數(shù)指定具體使用的限流算法。此外,SPI機(jī)制能夠?qū)U(kuò)展開放,常用于開源軟件,用戶可以實現(xiàn)自己的擴(kuò)展。

到此這篇關(guān)于詳解Java實現(xiàn)簡單SPI流程的文章就介紹到這了,更多相關(guān)Java實現(xiàn)SPI內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Boot項目集成UidGenerato的方法步驟

    Spring Boot項目集成UidGenerato的方法步驟

    這篇文章主要介紹了Spring Boot項目集成UidGenerato的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 使用Spring實現(xiàn)@Value注入靜態(tài)字段

    使用Spring實現(xiàn)@Value注入靜態(tài)字段

    這篇文章主要介紹了使用Spring實現(xiàn)@Value注入靜態(tài)字段方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringMVC框架中使用Filter實現(xiàn)請求日志打印方式

    SpringMVC框架中使用Filter實現(xiàn)請求日志打印方式

    這篇文章主要介紹了SpringMVC框架中使用Filter實現(xiàn)請求日志打印方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java中字符編碼格式詳解

    Java中字符編碼格式詳解

    在java應(yīng)用軟件中,會有多處涉及到字符集編碼,有些地方需要進(jìn)行正確的設(shè)置,有些地方需要進(jìn)行一定程度的處理。本文主要給大家講解java中字符的編碼格式等相關(guān)問題
    2016-03-03
  • @valid 無法觸發(fā)BindingResult的解決

    @valid 無法觸發(fā)BindingResult的解決

    這篇文章主要介紹了@valid 無法觸發(fā)BindingResult的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 在JDK和Eclipse下如何編寫和運(yùn)行Java Applet

    在JDK和Eclipse下如何編寫和運(yùn)行Java Applet

    本文主要介紹了在JDK和Eclipse的環(huán)境下如何編寫和運(yùn)行Java Applet,圖文方式,適合初學(xué)者學(xué)習(xí)。
    2015-09-09
  • java驗證電話號碼的方法

    java驗證電話號碼的方法

    這篇文章主要介紹了java驗證電話號碼的方法,需要的朋友可以參考下
    2014-02-02
  • SpringBoot添加SSL證書,開啟HTTPS方式(單向認(rèn)證服務(wù)端)

    SpringBoot添加SSL證書,開啟HTTPS方式(單向認(rèn)證服務(wù)端)

    這篇文章主要介紹了SpringBoot添加SSL證書,開啟HTTPS方式(單向認(rèn)證服務(wù)端),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Java連接服務(wù)器的兩種方式SFTP和FTP

    Java連接服務(wù)器的兩種方式SFTP和FTP

    在項目開發(fā)中,一般文件存儲很少再使用SFTP服務(wù),但是也不排除合作伙伴使用SFTP來存儲項目中的文件或者通過SFTP來實現(xiàn)文件數(shù)據(jù)的交互,這篇文章主要介紹了Java集成FTP與SFTP連接池
    2023-02-02
  • SpringBoot如何獲取application.properties中自定義的值

    SpringBoot如何獲取application.properties中自定義的值

    這篇文章主要介紹了SpringBoot獲取application.properties中的自定義的值,目錄結(jié)構(gòu)文件代碼給大家列舉的非常詳細(xì),需要的朋友可以參考下
    2021-09-09

最新評論

江阴市| 信丰县| 黔西| 阜宁县| 正宁县| 鄯善县| 页游| 通化县| 黎平县| 瑞昌市| 西畴县| 宝兴县| 繁峙县| 乐至县| 卢湾区| 永川市| 偏关县| 井冈山市| 两当县| 萍乡市| 汕头市| 蒲江县| 肥西县| 信阳市| 阜平县| 大港区| 民和| 札达县| 简阳市| 云和县| 湘西| 叶城县| 思茅市| 汤阴县| 宁化县| 白沙| 徐闻县| 通化县| 钦州市| 高雄县| 丰原市|