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

淺析Java SPI 與 dubbo SPI

 更新時間:2021年05月29日 08:46:05   作者:煢祇  
在Java中SPI是被用來設(shè)計給服務(wù)提供商做插件使用的。本文重點給大家介紹Java SPI 與 dubbo SPI的相關(guān)知識及區(qū)別介紹,感興趣的朋友跟隨小編一起學習下吧

Java原生SPI

面向接口編程+策略模式

實現(xiàn)

建立接口

Robot

public interface Robot {
    /**
     * 測試方法1
     */
    void sayHello();
}

多個實現(xiàn)類實現(xiàn)接口

RobotA

public class RobotA implements Robot {
    public RobotA() {
        System.out.println("Happy RobotA is loaded");
    }
    @Override
    public void sayHello() {
        System.out.println("i am a very very happy Robot ");
    }
    public void sayBye(){}
}

RobotB

public class RobotB implements Robot {
    public RobotB() {
        System.out.println("SB RobotB is loaded");
    }
    @Override
    public void sayHello() {
        System.out.println("i am a da sha bi ");
    }
    public void sayBye(){}
}

配置實現(xiàn)類與接口

META-INF/services目錄下建立一個以接口全限定名為名字的文件,里面的內(nèi)容是實現(xiàn)類的全限定名

原理

通過ServiceLoader與配置文件中的全限定名加載所有實現(xiàn)類,根據(jù)迭代器獲取具體的某一個類

我們通過對下面一段代碼的分析來說明

ServiceLoader<Robot> serviceLoader=ServiceLoader.load(Robot.class);
serviceLoader.forEach(Robot::sayHello);

load(Robot.class)這個方法的目的只是為了設(shè)置類加載器為線程上下文加載器,我們當然可以不這么做,直接調(diào)用load(Class service,ClassLoader loader)方法

public static <S> ServiceLoader<S> load(Class<S> service) {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

這個load方法其實也沒有做什么實質(zhì)的事,僅僅是實例化了一個ServiceLoad對象返回罷了

public static <S> ServiceLoader<S> load(Class<S> service,
                                        ClassLoader loader)
{
    return new ServiceLoader<>(service, loader);
}

那是不是構(gòu)造方法做了最核心的事呢?

private ServiceLoader(Class<S> svc, ClassLoader cl) {
    service = Objects.requireNonNull(svc, "Service interface cannot be null");
    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
    reload();
}
public void reload() {
    //這里的provider是一個對于已實例化對象的緩存,為Map類型
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

沒有,這里僅僅只是檢驗了參數(shù)和權(quán)限這樣一些準備操作.然后實例化了一個LazyIterator

這是LazyIterator的構(gòu)造函數(shù)

private LazyIterator(Class<S> service, ClassLoader loader) {
    this.service = service;
    this.loader = loader;
}

然后....,沒了,ServiceLoader<Robot> serviceLoader=ServiceLoader.load(Robot.class);執(zhí)行完畢了,到這里,并沒有實例化我們所需要的Robot對象,而僅僅只是返回了一個ServiceLoader對象

這時候如果我們?nèi)タ?code>serviceLoader的對象方法是這樣的

有用的只有這三個方法,reload上面已經(jīng)提到過,只是重新實例化一個對象而已.

而另外兩個iterator()是個迭代器,foreach也只是用于迭代的語法糖罷了.如果我們debug的話,會發(fā)現(xiàn)foreach的核心依舊會變成iterator(),好了,接下來重點看iterator()

public Iterator<S> iterator() {
    return new Iterator<S>() {

        Iterator<Map.Entry<String,S>> knownProviders
            = providers.entrySet().iterator();

        public boolean hasNext() {
            if (knownProviders.hasNext())
                return true;
            return lookupIterator.hasNext();
        }

        public S next() {
            if (knownProviders.hasNext())
                return knownProviders.next().getValue();
            return lookupIterator.next();
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

    };

這個方法實際上是返回了一個Iterator對象.而通過這個Iterator,我們可以遍歷獲取我們所需要的Robot對象.

我們來看其用于獲取對象的next方法

 public S next() {
            if (knownProviders.hasNext())
                return knownProviders.next().getValue();
            return lookupIterator.next();
        }

這個方法是先在緩存里找,緩存里找不到,就需要用最開始的實例化的lookupIterator

再來看看它的next方法

public S next() {
    if (acc == null) {
        return nextService();
    } else {
        PrivilegedAction<S> action = new PrivilegedAction<S>() {
            public S run() { return nextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}

這方法的核心是nextService,我們繼續(xù)看實現(xiàn),這個方法比較長,我貼一部分核心

if (!hasNextService())
    throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
    c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
    fail(service,
         "Provider " + cn + " not found");
}

hasNextService()判斷是否還可以繼續(xù)迭代,通過class.forName反射獲取實例,最后再加入到provider緩存中.于是基本邏輯就完成了.那nextName哪來的.是在hasNextService()中獲取的.

依舊只有核心代碼

//獲取文件
String fullName = PREFIX + service.getName();
if (loader == null)
    configs = ClassLoader.getSystemResources(fullName);
else
    configs = loader.getResources(fullName);
//解析文件配置
while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();

根據(jù)前綴(即META-INF/services)和接口的全限定名去找到對應(yīng)的配置文件.然后加載里面的配置,獲取具體實現(xiàn)類的名字.

Dubbo增強SPI

實現(xiàn)

建立接口

與原生SPI不同,dubbo需要加入@SPI注解

Robot

@SPI
public interface Robot {
    /**
     * 測試方法1
     */
    void sayHello();
}

多個實現(xiàn)類實現(xiàn)接口

RobotA

public class RobotA implements Robot {
    public RobotA() {
        System.out.println("Happy RobotA is loaded");
    }
    @Override
    public void sayHello() {
        System.out.println("i am a very very happy Robot ");
    }
    public void sayBye(){}
}

RobotB

public class RobotB implements Robot {
    public RobotB() {
        System.out.println("SB RobotB is loaded");
    }
    @Override
    public void sayHello() {
        System.out.println("i am a da sha bi ");
    }
    public void sayBye(){}
}

配置實現(xiàn)類與接口

META-INF/dubbo目錄下建立一個以接口全限定名為名字的文件,里面的內(nèi)容是自定義名字與類的全限定名的鍵值對,舉個例子

robotA = cn.testlove.double_dubbo.inter.impl.RobotA
robotB=cn.testlove.double_dubbo.inter.impl.RobotB

原理

我們通過對下列代碼的調(diào)用來進行分析

ExtensionLoader<Robot> extensionLoader= ExtensionLoader.getExtensionLoader(Robot.class);
Robot robotB = extensionLoader.getExtension("robotB");

第一句代碼沒什么好說的,只是獲取一個RobotExtensionLoader對象并且緩存在Map中,下次如果是同樣的接口可以直接從map中獲取

ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
    EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
    loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}

再來看第二句代碼

//從緩存中找
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
//雙重檢查
if (instance == null) {
    synchronized (holder) {
        instance = holder.get();
        if (instance == null) {
            instance = createExtension(name);
            holder.set(instance);
        }
    }
}

首先從緩存里找,找不到再創(chuàng)建一個新的對象。

再看createExtension(name)方法

Class<?> clazz = getExtensionClasses().get(name);

T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
    instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
    for (Class<?> wrapperClass : wrapperClasses) {
        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
    }
}
initExtension(instance);
return instance;

注意對于Class<?> clazz = getExtensionClasses().get(name);這一句的理解,這一句是獲取配置文件中所有類的Class實例,而不是獲取所有擴展類的實例。

接下來的流程其實也就簡單了從EXTENSION_INSTANCES緩存中獲取instance實例,如果沒有,就借助Class對象實例化一個,再放入緩存中

接著用這個instance去實例化一個包裝類然后返回.自此,一個我們需要的對象產(chǎn)生了.

最后我們看看getExtensionClasses()這個方法

Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
    synchronized (cachedClasses) {
        classes = cachedClasses.get();
        if (classes == null) {
            classes = loadExtensionClasses();
            cachedClasses.set(classes);
        }
    }
}
return classes;

這里的classes就是用來存各個擴展類Class的Map緩存,如果不存在的話,會調(diào)用loadExtensionClasses();去加載,剩下的就是找到對應(yīng)路徑下的配置文件,獲取全限定名了

上文我在分析Dubbo SPI時,多次提到Map,緩存二詞,我們可以具體有以下這些.其實看名字就大概知道作用了

private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>()    
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
private volatile Class<?> cachedAdaptiveClass = null;
private String cachedDefaultName;

對比原生的java SPI,dubbo的無疑更靈活,可以按需去加載某個類,也可以很便捷的通過自定義的名字去獲取類.而且Dubbo還支持setter注入.這點以后再講.

最后提一個問題,java原生的SPI只有在用iterator遍歷到的時候才會實例化對象,那能不能在遇到自己想要的實現(xiàn)對象時就停止遍歷,避免不必要的資源消耗呢?

補充:下面看下Dubbo SPI 和 Java SPI 區(qū)別?

JDK SPI

JDK 標準的 SPI 會一次性加載所有的擴展實現(xiàn),如果有的擴展吃實話很耗時,但

也沒用上,很浪費資源。

所以只希望加載某個的實現(xiàn),就不現(xiàn)實了

DUBBO SPI

1,對 Dubbo 進行擴展,不需要改動 Dubbo 的源碼

2,延遲加載,可以一次只加載自己想要加載的擴展實現(xiàn)。

3,增加了對擴展點 IOC 和 AOP 的支持,一個擴展點可以直接 setter 注入其它擴展點。

3,Dubbo 的擴展機制能很好的支持第三方 IoC 容器,默認支持 Spring Bean。

以上就是Java SPI 與 dubbo SPI的詳細內(nèi)容,更多關(guān)于Java SPI 與 dubbo SPI的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

台东市| 岑溪市| 壶关县| 禄丰县| 沈阳市| 英山县| 西平县| 平果县| 镇康县| 镇康县| 尚义县| 金堂县| 锦屏县| 长沙县| 诸城市| 阿鲁科尔沁旗| 乐平市| 五原县| 青冈县| 韩城市| 通许县| 翁牛特旗| 游戏| 永胜县| 马关县| 丽江市| 朔州市| 禄劝| 河池市| 星子县| 巴彦淖尔市| 宣恩县| 汤阴县| 三江| 开封市| 吉隆县| 楚雄市| 普格县| 阳东县| 延寿县| 如皋市|