教女朋友读懂系列:dubbo的可扩展机制SPI 源码分析
每晚10点与你分享Java技术、IT资讯
Dubbo SPI 架构图
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);Protocol http = extensionLoader.getExtension("dubbo");System.out.println(http);
Class<?> type:表示当前ExtensionLoader实例是哪个接口的扩展点加载器
ExtensionFactory objectFactory:扩展点工厂(对象工厂),可以获得某个对象
ExtensionLoader最终所得到的对象是Dubbo SPI机制产生的
ExtensionFactory最终所得到的对象可能是Dubbo SPI机制所产生的,也可能是从Spring容器中所获得的对象
getExtension("dubbo"):表示获取名字为dubbo的扩展点实例
getAdaptiveExtension():表示获取一个自适应的扩展点实例
getActivateExtension(URL url, String[] values, String group):表示一个可以被url激活的扩展点实例,后文详细解释
getExtension(String name)方法
createExtension(String name)方法
根据name找到对应的扩展点实现类
根据实现类生成一个实例,把实现类和对应生成的实例进行缓存
对生成出来的实例进行依赖注入(给实例的属性进行赋值)
对依赖注入后的实例进行AOP(Wrapper),把当前接口类的所有的Wrapper全部一层一层包裹在实例对象上,没包裹个Wrapper后,也会对Wrapper对象进行依赖注入
返回最终的Wrapper对象
getExtensionClasses
根据接口的全限定名去META-INF/dubbo/internal/目录下寻找对应的文件,调用loadResource方法进行加载
根据接口的全限定名去META-INF/dubbo/目录下寻找对应的文件,调用loadResource方法进行加载
根据接口的全限定名去META-INF/services/目录下寻找对应的文件,调用loadResource方法进行加载
loadResource方法
loadClass方法
当前扩展点实现类上是否存在@Adaptive注解,如果存在则把该类认为是当前接口的默认自适应类(接口代理类),并把该类存到cachedAdaptiveClass属性上。
当前扩展点实现是否是一个当前接口的一个Wrapper类,如果判断的?就是看当前类中是否存在一个构造方法,该构造方法只有一个参数,参数类型为接口类型,如果存在这一的构造方法,那么这个类就是该接口的Wrapper类,如果是,则把该类添加到cachedWrapperClasses中去, cachedWrapperClasses是一个set。
如果不是自适应类,或者也不是Wrapper类,则判断是有存在name,如果没有name,则报错。
如果有多个name,则判断一下当前扩展点实现类上是否存在@Activate注解,如果存在,则把该类添加到cachedActivates中,cachedWrapperClasses是一个map。
最后,遍历多个name,把每个name和对应的实现类存到extensionClasses中去,extensionClasses就是上文所提到的map。
Dubbo中的IOC
根据当前实例的类,找到这个类中的setter方法,进行依赖注入
先分析出setter方法的参数类型pt
在截取出setter方法所对应的属性名property
调用objectFactory.getExtension(pt, property)得到一个对象,这里就会从Spring容器或通过DubboSpi机制得到一个对象,比较特殊的是,如果是通过DubboSpi机制得到的对象,是pt这个类型的一个自适应对象(代理对象)。
再反射调用setter方法进行注入
Dubbo中的AOP
createAdaptiveExtensionClass方法
type就是接口
cacheDefaultName就是该接口默认的扩展点实现的名字
package org.apache.dubbo.rpc;import org.apache.dubbo.common.extension.ExtensionLoader;public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); } public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException { if (arg0 == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null"); org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); if(extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0); } public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg1; String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); }}
package org.apache.dubbo.rpc;import org.apache.dubbo.common.extension.ExtensionLoader;public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); } public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException { if (arg0 == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null"); org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); if(extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0); } public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg1; String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); }}
该方法如果是无参的,那么则会报错
该方法有参数,可以有多个,并且其中某个参数类型是URL,那么则可以进行代理
该方法有参数,可以有多个,但是没有URL类型的参数,那么则不能进行代理
该方法有参数,可以有多个,没有URL类型的参数,但是如果这些参数类型,对应的类中存在getUrl方法(返回值类型为URL),那么也可以进行代理
demo
ExtensionLoader<Filter> extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);URL url = new URL("http://", "localhost", 8080);url = url.addParameter("cache", "test");List<Filter> activateExtensions = extensionLoader.getActivateExtension(url,
new String[]{"validation"}, CommonConstants.CONSUMER);for (Filter activateExtension : activateExtensions) { System.out.println(activateExtension);}
String[] group():表示这个扩展点是属于哪组的,这里组通常分为PROVIDER和CONSUMER,表示该扩展点能在服务提供者端,或者消费端使用
String[] value():表示的是URL中的某个参数key,当利用getActivateExtension方法来寻找扩展点时,如果传入的url中包含的参数的所有key中,包括了当前扩展点中的value值,那么则表示当前url可以使用该扩展点。
●
●
●
●