日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

淺析Java SPI 與 dubbo SPI

瀏覽:22日期:2022-08-11 15:56:31
Java原生SPI

面向接口編程+策略模式

實現

建立接口

Robot

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

多個實現類實現接口

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(){}}

配置實現類與接口

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

原理

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

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

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

load(Robot.class)這個方法的目的只是為了設置類加載器為線程上下文加載器,我們當然可以不這么做,直接調用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方法其實也沒有做什么實質的事,僅僅是實例化了一個ServiceLoad對象返回罷了

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

那是不是構造方法做了最核心的事呢?

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); }

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

這是LazyIterator的構造函數

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

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

這時候如果我們去看serviceLoader的對象方法是這樣的

淺析Java SPI 與 dubbo SPI

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

而另外兩個iterator()是個迭代器,foreach也只是用于迭代的語法糖罷了.如果我們debug的話,會發現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,我們繼續看實現,這個方法比較長,我貼一部分核心

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()判斷是否還可以繼續迭代,通過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();

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

Dubbo增強SPI

實現

建立接口

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

Robot

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

多個實現類實現接口

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(){}}

配置實現類與接口

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

robotA = cn.testlove.double_dubbo.inter.impl.RobotArobotB=cn.testlove.double_dubbo.inter.impl.RobotB

原理

我們通過對下列代碼的調用來進行分析

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

第一句代碼沒什么好說的,只是獲取一個Robot的ExtensionLoader對象并且緩存在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);} }}

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

再看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去實例化一個包裝類然后返回.自此,一個我們需要的對象產生了.

最后我們看看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緩存,如果不存在的話,會調用loadExtensionClasses();去加載,剩下的就是找到對應路徑下的配置文件,獲取全限定名了

上文我在分析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遍歷到的時候才會實例化對象,那能不能在遇到自己想要的實現對象時就停止遍歷,避免不必要的資源消耗呢?

補充:下面看下Dubbo SPI 和 Java SPI 區別?

JDK SPI

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

也沒用上,很浪費資源。

所以只希望加載某個的實現,就不現實了

DUBBO SPI

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

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

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

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

以上就是Java SPI 與 dubbo SPI的詳細內容,更多關于Java SPI 與 dubbo SPI的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
日韩一区欧美二区| 国产日韩亚洲欧美精品| 91成人网在线观看| 久久久久国产精品一区三寸| 欧洲av不卡| 日韩免费福利视频| 偷拍亚洲精品| 精品欧美视频| 成人精品动漫一区二区三区| 国产伦精品一区二区三区千人斩| 视频一区日韩精品| 欧美综合国产| 欧美少妇精品| 成人在线视频中文字幕| 婷婷综合亚洲| 久久免费黄色| 亚洲精品乱码日韩| 亚洲精品极品少妇16p| 丝袜亚洲精品中文字幕一区| 欧美日韩在线二区| 日本麻豆一区二区三区视频| 亚洲精选久久| 999国产精品视频| 日韩欧美午夜| 久久夜夜操妹子| 国产精品.xx视频.xxtv| 欧美在线精品一区| 青青国产91久久久久久| 极品日韩av| 天堂va在线高清一区| 日韩高清一区二区| 日韩av在线播放中文字幕| 欧美黄色一区| 狠狠色综合网| 国产精品一线| 午夜性色一区二区三区免费视频| 成年男女免费视频网站不卡| 美女视频一区在线观看| 1024精品一区二区三区| 日韩美女国产精品| 亚洲资源av| 四虎4545www国产精品| 久久成人福利| 久久99伊人| 婷婷激情久久| 精品欧美视频| 久久久久久一区二区| 精品一区二区三区的国产在线观看| 国产午夜久久av| 香蕉精品视频在线观看| 国产精品极品国产中出| 蜜臀av一区二区在线免费观看| 亚洲影院天堂中文av色| 国产亚洲一区二区手机在线观看| 久久av在线| 欧美国产另类| 国产精品一区二区99| 999国产精品永久免费视频app| 亚洲另类黄色| 精品三级在线| 日韩精品三级| 最新国产精品视频| 99在线|亚洲一区二区| 日韩在线一二三区| 国产综合亚洲精品一区二| 国产精品一区二区免费福利视频 | 久色成人在线| 欧美专区一区| 激情综合网五月| 麻豆精品蜜桃视频网站| 久久久夜精品| 成人国产精品久久| 石原莉奈一区二区三区在线观看 | 中文字幕中文字幕精品| 久久精品国产久精国产爱| 久久91视频| 亚洲精品成人一区| 国产色99精品9i| 日韩精品中文字幕一区二区| 亚洲有吗中文字幕| 亚州国产精品| 婷婷丁香综合| 日韩一区二区三区免费播放| 国产极品嫩模在线观看91精品| 精品国产不卡一区二区| 国产aⅴ精品一区二区三区久久| 免费在线观看精品| 日韩国产欧美| 亚洲三级国产| 黑人精品一区| 婷婷综合国产| 国产欧美一区二区三区精品观看 | 老司机免费视频一区二区三区| 国产亚洲精品久久久久婷婷瑜伽| 日本欧美不卡| 在线人成日本视频| 日本va欧美va欧美va精品| 日韩亚洲在线| 日韩中文字幕视频网| | 国模精品一区| 中文精品在线| 久久国产66| 精品1区2区3区4区| 亚洲激精日韩激精欧美精品| 欧美在线资源| 美女国产精品久久久| 日本一二区不卡| 成人免费电影网址| 亚洲视频国产| 国产欧美日韩精品一区二区三区| 精品色999| 欧洲av不卡| 亚洲精品乱码日韩| 国产精品免费不| sm捆绑调教国产免费网站在线观看| 另类欧美日韩国产在线| 成人台湾亚洲精品一区二区| 国产精品成人a在线观看| 精品国产欧美日韩| 久久蜜桃精品| 蜜臀久久精品| 亚洲三级国产| 91看片一区| 亚洲美洲欧洲综合国产一区| 国产剧情在线观看一区| 国产成人免费视频网站视频社区| 国产videos久久| 日韩欧美视频专区| 国产精品久久久久久久久久齐齐| 国产精品亚洲欧美一级在线| 91tv亚洲精品香蕉国产一区| 亚洲高清av| 日韩午夜一区| 日韩一区欧美二区| 视频在线观看一区二区三区| 国产丝袜一区| 欧美激情在线精品一区二区三区| 久久久人人人| 日韩精品影视| 亚洲欧洲av| 国产免费av国片精品草莓男男| 亚洲精品精选| 中文在线а√在线8| 欧美日韩中文一区二区| 亚洲网址在线观看| 国产精品亚洲一区二区在线观看| 国产精品红桃| 欧美激情网址| 亚洲福利专区| 欧美日本精品| 日韩亚洲一区在线| 日本成人精品| 久久国产99| 日韩午夜电影| 午夜在线精品| 久久影院一区| 久久精品国产在热久久| 91高清一区| 日韩国产一区二区| 国产精品久久观看| 亚洲影院天堂中文av色| 日韩欧美一区二区三区免费观看| 日韩欧美三区| 国产精品地址| 日韩av午夜在线观看| 午夜亚洲福利| a国产在线视频| 蜜桃av一区二区| av高清不卡| 国产麻豆一区二区三区精品视频| 尹人成人综合网| 国内精品伊人| 青青草视频一区| 美女网站久久| 日韩欧美1区| 国产成人免费av一区二区午夜| 欧美激情综合| 美女视频黄免费的久久| 国产精品美女午夜爽爽| 国产精品久久久久蜜臀| 日韩黄色在线观看| 97久久精品| 日本久久黄色| 色偷偷偷在线视频播放| 国内精品伊人| 里番精品3d一二三区| 国产日韩一区二区三区在线| 91精品久久久久久久久久不卡| 中文一区一区三区高中清不卡免费| 国产精品亚洲欧美日韩一区在线 | 精品免费av一区二区三区| 99国产精品视频免费观看一公开| 精品99久久| 国产麻豆精品| 日本少妇精品亚洲第一区| 蜜臀91精品国产高清在线观看| 国产精品成人3p一区二区三区| 亚洲色图网站| 日韩精品亚洲专区|