首页 > 代码库 > Dubbo源码分析系列---扩展点加载

Dubbo源码分析系列---扩展点加载

扩展点配置:

约定:

在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。(摘自dubbo文档)

示例:

假如我现在想使用自己定义的协议Myprotocol,在resources目录下新建META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol目录文件,文件内容定义:

myprotocol=com.selrain.MyProtocol

实现类内容:

public class MyProtocol implements Protocol {    @Override    public int getDefaultPort() {        return 1111111;    }   ...}

在我们的xml配置protocol属性时就可以配置myprotocol啦,现在为了测试我们手动获取下:

@SpringBootApplicationpublic class Chapter2Application {    public static void main(String[] args) {        SpringApplication.run(Chapter2Application.class, args);        Protocol p= ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("myprotocol");        System.out.print(p.getDefaultPort());    }}住:为了少写点代码直接上spring boot了

看看自己的控制台输出吧,是不是调用了我们自定义的类了?

 

扩展点自动包装:

自动Wrap扩展点的Wrapper类
ExtensionLoader会把加载扩展点时(通过扩展点配置文件中内容),如果该实现有拷贝构造函数,则判定为扩展点Wrapper类。

Wrapper类同样实现了扩展点接口(摘自dubbo文档)

什么意思呢?比方说Dubbo里面定义了ProtocolListenerWrapper、ProtocolFilterWrapper2各Wrapper类:

public class ProtocolListenerWrapper implements Protocol {    private final Protocol protocol;    public ProtocolListenerWrapper(Protocol protocol){        if (protocol == null) {            throw new IllegalArgumentException("protocol == null");        }        this.protocol = protocol;    }    ...    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {            return protocol.export(invoker);        }        return new ListenerExporterWrapper<T>(protocol.export(invoker),                 Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)                        .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));    }    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {            return protocol.refer(type, url);        }        return new ListenerInvokerWrapper<T>(protocol.refer(type, url),                 Collections.unmodifiableList(                        ExtensionLoader.getExtensionLoader(InvokerListener.class)                        .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));    }   ...}public class ProtocolFilterWrapper implements Protocol {    private final Protocol protocol;    public ProtocolFilterWrapper(Protocol protocol){        if (protocol == null) {            throw new IllegalArgumentException("protocol == null");        }        this.protocol = protocol;    }    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {       ...        return protocol.export(invoker);    }    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {       ...       return protocol.refer(type, url);    }    public void destroy() {        protocol.destroy();    }   ...    }

2个Wrapper类共同点就是都实现了Protocol接口,构造函数也都时Protocol参数,这样做的好处是可以通过层层包装来使各个类的逻辑处理分开;

ProtocolListenerWrapper===》ProtocolFilterWrapper===》DubboProtocol,Protocol的调用过程就是这样的。

现在来测试一下:

@SpringBootApplicationpublic class Chapter2Application {    public static void main(String[] args) {        SpringApplication.run(Chapter2Application.class, args);        Protocol p= ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("myprotocol");        System.out.print(p.getClass().getSimpleName());    }}

控制台输出了ProtocolListenerWrapper,也就是说当我们调用myprotocol的方法时,会先经过层层包装,处理,最后才调用myprotocol的方法,当然这个Wrapper我们自己也可以定义,当我们需要额外的做一些逻辑处理的时候,和上面的扩展点配置一样。

扩展点自动装配:

加载扩展点时,扩展点实现类的成员如果为其它扩展点类型,ExtensionLoader在会自动注入依赖的扩展点

对应文章底部图中的injectExtension方法

private T injectExtension(T instance) {        try {            if (objectFactory != null) {                for (Method method : instance.getClass().getMethods()) {                    if (method.getName().startsWith("set")                            && method.getParameterTypes().length == 1                            && Modifier.isPublic(method.getModifiers())) {                        Class<?> pt = method.getParameterTypes()[0];                        try {                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";                            Object object = objectFactory.getExtension(pt, property);                            if (object != null) {                                method.invoke(instance, object);                            }                        ...    }

objectFactory 也是基于dubbo的spi扩展机制获取,它有3个实现类,分别是AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory

objectFactory 在我们初始化ExtensionLoader类的时候已经初始化:(因为AdaptiveExtensionFactory打上了Adaptive注解,所以值为AdaptiveExtensionFactory)

private ExtensionLoader(Class<?> type) {        this.type = type;        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());    }

AdaptiveExtensionFactory持有所有ExtensionFactory对象的集合,dubbo内部默认实现的对象工厂是SpiExtensionFactory和SpringExtensionFactory,他们经过TreeMap排好序的查找顺序是优先先从SpiExtensionFactory获取,如果返回空在从SpringExtensionFactory获取。

1) SpiExtensionFactory工厂获取要被注入的对象,就是要获取dubbo spi扩展的实现,所以传入的参数类型必须是接口类型并且接口上打上了@SPI注解,返回的是一个设配类对象。

public class SpiExtensionFactory implements ExtensionFactory {    public <T> T getExtension(Class<T> type, String name) {        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);            if (loader.getSupportedExtensions().size() > 0) {                return loader.getAdaptiveExtension();            }        }        return null;    }}

 2)SpringExtensionFactory,Dubbo利用spring的扩展机制跟spring做了很好的融合。在发布或者去引用一个服务的时候,会把spring的容器添加到SpringExtensionFactory工厂集合中去, 当SpiExtensionFactory没有获取到对象的时候会遍历SpringExtensionFactory中的spring容器来获取要注入的对象

public class SpringExtensionFactory implements ExtensionFactory {    ...    @SuppressWarnings("unchecked")    public <T> T getExtension(Class<T> type, String name) {        for (ApplicationContext context : contexts) {            if (context.containsBean(name)) {                Object bean = context.getBean(name);                if (type.isInstance(bean)) {                    return (T) bean;                }            }        }        return null;    }

扩展点自适应

扩展点的Adaptive实例
ExtensionLoader注入的依赖扩展点是一个Adaptive实例,直到扩展点方法执行时才决定调用是一个扩展点实现。
Dubbo使用URL对象(包含了Key-Value)传递配置信息。
扩展点方法调用会有URL参数(或是参数有URL成员)
这样依赖的扩展点也可以从URL拿到配置信息,所有的扩展点自己定好配置的Key后,配置信息从URL上从最外层传入。URL在配置传递上即是一条总线(摘自Dubbo官方文档)

比如我们的Protocol接口,由于没有实现类打上Adaptive注解,所以生成javassist字节码的方式创建的实现类:

package com.alibaba.dubbo.rpc;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {    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.getProtocol() == null ? "dubbo" : url.getProtocol() ); if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0);}public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1; String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1);}}

可以看到通过url传播,在url中拿到最终扩展点名字,最终调用扩展点的export方法

 

内部实现(重要方法):

技术分享

 

参考:

http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577235

 

Dubbo源码分析系列---扩展点加载