2019獨角獸企業重金招聘Python工程師標準>>>
本文重點講述SPI機制,從jdk和dubbo
1、jdk spi機制
2、dubbo spi實現
首先spi是什么?
SPI是為某個接口尋找服務實現的機制。為了實現在模塊裝配的時候能不在程序里動態指明,這就需要一種服務發現機制。
其次java spi是怎么找到實現類的?
java spi和所有實現接口的廠商有一個俗稱的約定,只要將META-INF/services文件夾下生成一個和抽象類全名稱(路徑+類名稱)相同的配置文件,那么廠商的jar包只要在工程路徑下就能找到實現類。這種方式主要是解決不同廠商不同實現類的加載問題。在不修改java文件的情況下,如何才能夠定位到不同的實現類。
JDK的spi機制有一個缺點,就是如果多個廠商的spi實現的jar包都在路徑下,那么就要加載所有的實現類,這樣很浪費資源。dubbo中每一種接口都有很多種實現,如果使用jdk這種方式自然做不到優雅的根據一個接口來獲得該接口的實現。dubbo的目標就是:根據你配置的name獲取某一個特定的接口實現,沒有用到的其他接口實現就不能被實例化,免得浪費。因此,dubbo就按照SPI機制的原理自己實現了一套擴展機制。為實現dubbo的擴展機制,dubbo的SPI做到了一下三個方面。
1?可以方便的獲取某一個想要的擴展實現,java的SPI機制就沒有提供這樣的功能
2?對于擴展實現IOC???依賴注入功能
3?對擴展采用裝飾器模式進行功能增強,類似AOP實現的功能
dubbo的SPI實現具體如下。
首先定義一個SPI注解類
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
/**
? ? * 缺省擴展點名。
? ? */
String value() default "";
} dubbo里面很多的接口都打了SPI注解,這些注解的實現要從一下三個文件夾下去尋找實現。
META-INF/dubbo/internal/ ? //dubbo內部實現的各種擴展都放在了這個目錄了
META-INF/dubbo
/META-INF/services/
文件里面的內容全部都是key-value的形式存在。dubbo的各種接口有很多類型的實現。拿Protocol舉例,它的實現:DubboProtocol InjvmProtocolHessianProtocol WebServiceProtocol等等。dubbo的擴展機制如何去查找你的實現類,并實現調用的呢?
ExtensionLoader<T>
拿ServiceConfig<T>中的這兩個變量舉例子。
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
這兩個變量最終生成的既不是接口也不是具體的實現類,而是一個接口適配器類。這個適配器類動態的生成的,如下所示的代碼:
package?com.alibaba.dubbo.rpc;
import?com.alibaba.dubbo.common.extension.ExtensionLoader;
public?class?ProxyFactory$Adpative?implements?com.alibaba.dubbo.rpc.ProxyFactory?{
public?java.lang.Object?getProxy(com.alibaba.dubbo.rpc.Invoker?arg0)?throws?com.alibaba.dubbo.rpc.Invoker?{
if?(arg0?==?null)?throw?new?IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker?argument?==?null");
if?(arg0.getUrl()?==?null)?throw?new?IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker?argument?getUrl()?==?null");com.alibaba.dubbo.common.URL?url?=?arg0.getUrl();
String?extName?=?url.getParameter("proxy",?"javassist");
if(extName?==?null)?throw?new?IllegalStateException("Fail?to?get?extension(com.alibaba.dubbo.rpc.ProxyFactory)?name?from?url("?+?url.toString()?+?")?use?keys([proxy])");
//根據url的信息去獲取真實的實現類
com.alibaba.dubbo.rpc.ProxyFactory?extension?=?(com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return?extension.getProxy(arg0);
}
public?com.alibaba.dubbo.rpc.Invoker?getInvoker(java.lang.Object?arg0,?java.lang.Class?arg1,?com.alibaba.dubbo.common.URL?arg2)?throws?java.lang.Object?{
if?(arg2?==?null)?throw?new?IllegalArgumentException("url?==?null");
com.alibaba.dubbo.common.URL?url?=?arg2;
String?extName?=?url.getParameter("proxy",?"javassist");
if(extName?==?null)?throw?new?IllegalStateException("Fail?to?get?extension(com.alibaba.dubbo.rpc.ProxyFactory)?name?from?url("?+?url.toString()?+?")?use?keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory?extension?=?(com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return?extension.getInvoker(arg0,?arg1,?arg2);
}
}
首先,我們來看適配器類是如何生成
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
第一步:通過proxyFactory.class類型生成一個ExtensionLoader<T>實例。
public?static?<T>?ExtensionLoader<T>?getExtensionLoader(Class<T>?type)?{
ExtensionLoader<T>?loader?=?(ExtensionLoader<T>)?EXTENSION_LOADERS.get(type);
if?(loader?==?null)?{每個定義的spi的接口都會構建一個ExtensionLoader實例,存儲在EXTENSION_LOADERS
EXTENSION_LOADERS.putIfAbsent(type,?new?ExtensionLoader<T>(type));loader?=?(ExtensionLoader<T>)?EXTENSION_LOADERS.get(type);
}
return?loader;}
第二步:創建Adaptive實例,放入數組cachedAdaptiveInstance中。
public?T?getAdaptiveExtension()?{Object?instance?=?cachedAdaptiveInstance.get();
if?(instance?==?null)?{
if(createAdaptiveInstanceError?==?null)?{
synchronized?(cachedAdaptiveInstance)?{instance?=?cachedAdaptiveInstance.get();
if?(instance?==?null)?{
try?{//創建Adaptive實例,放入數組cachedAdaptiveInstance中。instance?=?createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);}?catch?(Throwable?t)?{
createAdaptiveInstanceError?=?t;
throw?new?IllegalStateException("fail?to?create?adaptive?instance:?"?+?t.toString(),?t);}}}}
else?{
throw?new?IllegalStateException("fail?to?create?adaptive?instance:?"?+?createAdaptiveInstanceError.toString(),?createAdaptiveInstanceError);}}
return?(T)?instance;
}private?T?createAdaptiveExtension()?{
try?{
return?injectExtension((T)?getAdaptiveExtensionClass().newInstance());}?catch?(Exception?e)?{
throw?new?IllegalStateException("Can?not?create?adaptive?extenstion?"?+?type?+?",?cause:?"?+?e.getMessage(),?e);}
}private?Class<?>?createAdaptiveExtensionClass()?{
//生成類適配器,String?code?=?createAdaptiveExtensionClassCode();
ClassLoader?classLoader?=?findClassLoader();com.alibaba.dubbo.common.compiler.Compiler?compiler?=?ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();//編譯這個類適配器為class文件
return?compiler.compile(code,?classLoader);
}
//?此方法已經getExtensionClasses方法同步過。
private?Map<String,?Class<?>>?loadExtensionClasses()?{//先讀取SPI注解的value值,有值作為默認擴展實現的key
final?SPI?defaultAnnotation?=?type.getAnnotation(SPI.class);
if(defaultAnnotation?!=?null)?{String?value?=?defaultAnnotation.value();
if(value?!=?null?&&?(value?=?value.trim()).length()?>?0)?{String[]?names?=?NAME_SEPARATOR.split(value);
if(names.length?>?1)?{
throw?new?IllegalStateException("more?than?1?default?extension?name?on?extension?"?+?type.getName()+?":?"?+?Arrays.toString(names));}
if(names.length?==?1)?cachedDefaultName?=?names[0];}}Map<String,?Class<?>>?extensionClasses?=?new?HashMap<String,?Class<?>>();//讀取三個文件夾下的文件,將key-class放置到extensionClasses中loadFile(extensionClasses,?DUBBO_INTERNAL_DIRECTORY);loadFile(extensionClasses,?DUBBO_DIRECTORY);loadFile(extensionClasses,?SERVICES_DIRECTORY);
return?extensionClasses;
}
private?void?loadFile(Map<String,?Class<?>>?extensionClasses,?String?dir)?{
//獲取文件路徑?目錄+type名稱String?fileName?=?dir?+?type.getName();try?{Enumeration<java.net.URL>?urls;//類加載器ClassLoader?classLoader?=?findClassLoader();if?(classLoader?!=?null)?{urls?=?classLoader.getResources(fileName);}?else?{urls?=?ClassLoader.getSystemResources(fileName);}if?(urls?!=?null)?{while?(urls.hasMoreElements())?{java.net.URL?url?=?urls.nextElement();try?{BufferedReader?reader?=?new?BufferedReader(new?InputStreamReader(url.openStream(),?"utf-8"));try?{String?line?=?null;while?((line?=?reader.readLine())?!=?null)?{final?int?ci?=?line.indexOf('#');if?(ci?>=?0)?line?=?line.substring(0,?ci);line?=?line.trim();if?(line.length()?>?0)?{try?{String?name?=?null;int?i?=?line.indexOf('=');if?(i?>?0)?{name?=?line.substring(0,?i).trim();line?=?line.substring(i?+?1).trim();}if?(line.length()?>?0)?{
//讀取到類Class<?>?clazz?=?Class.forName(line,?true,?classLoader);
//判斷實現類是否實現了type接口if?(!?type.isAssignableFrom(clazz))?{throw?new?IllegalStateException("Error?when?load?extension?class(interface:?"?+type?+?",?class?line:?"?+?clazz.getName()?+?"),?class?"?+?clazz.getName()?+?"is?not?subtype?of?interface.");}
//類中是否有方法打了Adaptive注解if?(clazz.isAnnotationPresent(Adaptive.class))?{
//將打了Adaptive注解的類放置到cachedAdaptiveClass?中去。if(cachedAdaptiveClass?==?null)?{cachedAdaptiveClass?=?clazz;}else?if?(!?cachedAdaptiveClass.equals(clazz))?{throw?new?IllegalStateException("More?than?1?adaptive?class?found:?"+?cachedAdaptiveClass.getClass().getName()+?",?"?+?clazz.getClass().getName());}}?else?{//判斷該是否有實現類是否存在入參為接口的構造器clazz.getConstructor(type);Set<Class<?>>?wrappers?=?cachedWrapperClasses;if?(wrappers?==?null)?{cachedWrapperClasses?=?new?ConcurrentHashSet<Class<?>>();wrappers?=?cachedWrapperClasses;}//按照裝飾器的角色將該類加進來wrappers.add(clazz);}?//?end?of?while?urls}}?catch?(Throwable?t)?{logger.error("Exception?when?load?extension?class(interface:?"?+type?+?",?description?file:?"?+?fileName?+?").",?t);}
}
適配器中發現這個實現類也是由這個方法生成extension?=?(com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName)
//創建擴展類
instance?=?createExtension(name);
private?T?createExtension(String?name)?{
//加載類Class<?>?clazz?=?getExtensionClasses().get(name);if?(clazz?==?null)?{throw?findException(name);}try?{//從容器中獲取是否存在T?instance?=?(T)?EXTENSION_INSTANCES.get(clazz);if?(instance?==?null)?{//容器中不存在,則按照類-實例對的形式放置到容器中EXTENSION_INSTANCES.putIfAbsent(clazz,?(T)?clazz.newInstance());//獲取該實例數據對instance?=?(T)?EXTENSION_INSTANCES.get(clazz);}//set注入參數injectExtension(instance);//獲取包裝器類。class?com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper???class?com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapperSet<Class<?>>?wrapperClasses?=?cachedWrapperClasses;if?(wrapperClasses?!=?null?&&?wrapperClasses.size()?>?0)?{for?(Class<?>?wrapperClass?:?wrapperClasses)?{
//將實例對更新為有包裝器類的實例。?????????????instance?=?injectExtension((T)?wrapperClass.getConstructor(type).newInstance(instance));}}return?instance;}?catch?(Throwable?t)?{throw?new?IllegalStateException("Extension?instance(name:?"?+?name?+?",?class:?"?+type?+?")??could?not?be?instantiated:?"?+?t.getMessage(),?t);}
}
dubbo的SPI擴展機制,使得我們給出url就能動態的去獲取真實的實現類。獲得到真實的實現類,是實現功能的第一步。下面我們來看看spring如何加載到dubbo解析器類,并將dubbo功能收入囊下的。未完待續。
參考文獻
http://m.blog.csdn.net/blog/u010311445/41577235
轉載于:https://my.oschina.net/zjItLife/blog/530923
總結
以上是生活随笔為你收集整理的dubboSPI机制浅谈的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。