Dubbo SPI 之 @Adaptive 固定已知类扩展未知类

咱们已知Adaptive是一个注解,经过 @Target({ElementType.TYPE, ElementType.METHOD}) 可知该注解能够用在类定义或方法定义上。java

/**
 * Activate
 * <p/>
 * 对于能够被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件。
 * 好比,过滤扩展,有多个实现,使用Activate Annotation的扩展能够根据条件被自动加载。
 * <ol>
 * <li>{@link Activate#group()}生效的Group。具体的有哪些Group值由框架SPI给出。
 * <li>{@link Activate#value()}在{@link com.alibaba.dubbo.common.URL}中Key集合中有,则生效。
 * </ol>
 * <p>
 * <p/>
 * 底层框架SPI提供者经过{@link com.alibaba.dubbo.common.extension.ExtensionLoader}的{@link ExtensionLoader#getActivateExtension}方法
 * 得到条件的扩展。
 *
 * @author william.liangf
 * @author ding.lid
 * @export
 * @see SPI
 * @see ExtensionLoader
 * @see ExtensionLoader#getActivateExtension(com.alibaba.dubbo.common.URL, String[], String)
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    /**
     * Group过滤条件。
     * <br />
     * 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
     * <br />
     * 如没有Group设置,则不过滤。
     */
    String[] group() default {};

    /**
     * Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。
     * <p/>
     * 示例:<br/>
     * 注解的值 <code>@Activate("cache,validatioin")</code>,
     * 则{@link ExtensionLoader#getActivateExtension}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展。
     * <br/>
     * 如没有设置,则不过滤。
     */
    String[] value() default {};

    /**
     * 排序信息,能够不提供。
     */
    String[] before() default {};

    /**
     * 排序信息,能够不提供。
     */
    String[] after() default {};

    /**
     * 排序信息,能够不提供。
     */
    int order() default 0;
}

它的设计目的是为了实现Dubbo SPI 时用来固定已知的类和扩展未知类。spring

注解在接口的实现类上和方法上的区别:

1.注解在接口的实现类上:表明人工实现,实现一个装饰类,它主要用于固定已知类,目前整个系统只有两个,AdaptiveCompiler、AdaptiveExtensionFactory。设计模式

@SPI("javassist")
public interface Compiler {

    /**
     * Compile java source code.
     *
     * @param code        Java source code
     * @param classLoader TODO
     * @return Compiled class
     */
    Class<?> compile(String code, ClassLoader classLoader);

}

项目继承图以下:app

a. 为何AdaptiveCompiler这个类是固定已知的?
由于整个框架仅支持Javassist和JdkCompiler;
b. 为何AdaptiveExtensionFactory这个类是固定已知的?
由于整个框架仅支持2个objFactory,一个是spi,另外一个是spring;框架

2.注解在方法上,表明自动生成和编译一个动态的Adaptive类,每一个方法均可以根据方法参数动态获取各自的扩展点,主要因为SPI 获取类为不固定的位置的扩展类,因此设计了动态的$Adaptive类。jvm

例如 Protocol的spi类有injvm、dubbo、registry、filter、listener等不少未知扩展类,它设计了Protocol$Adaptive的类,再经过ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi类);来提取对象。ide

在dubbo中通常会首先经过ExtensionLoader.getAdaptiveExtension获取Adaptive扩展。这个方法会首先在扩展点接口的全部实现类中查找类上是否有含有@Adaptive注解,若是有这样的类直接返回该类的实例,若是没有则会查找扩展点接口的方法是否有@Adaptive注解并动态编译一个类实现该接口并扩展这些含有@Adaptive注解的方法。函数

代码执行流程:ui

-----------------------getAdaptiveExtension()
-->getAdaptiveExtension()//为cachedAdaptiveInstance赋值
  -->createAdaptiveExtension()
    -->getAdaptiveExtensionClass()
      -->getExtensionClasses()//为cachedClasses 赋值
        -->loadExtensionClasses()
          -->loadFile
      -->createAdaptiveExtensionClass()//自动生成和编译一个动态的adpative类,这个类是一个代理类
        -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
        -->compiler.compile(code, classLoader)
    -->injectExtension()//做用:进入IOC的反转控制模式,实现了动态入注

Protocol全部扩展实现类上都没有@Adaptive注解,且扩展接口含有两个 @Adaptive 注解的方法:exporter() refer(),因此dubbo会生成一个动态类Protocol$Adaptive,且它实现Protocol接口来扩展这两个Adaptive方法。扩展点接口和最终动态生成Protocol$Adaptive类以下:this

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    void destroy();

}
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }
    public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }
    public com.alibaba.dubbo.rpc.Invoker refer(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);
    }
    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);
    }
}

String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );从arg0中解析出扩展点名称extName,extName的默认值为@SPI的value。这是adaptive的精髓:每个方法均可以根据方法参数动态获取各自须要的扩展点。

Protocol extension =(Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).
getExtension(extName);根据extName从新获取指定的Protocol.class扩展点。若是全部扩展点中含有Wrapper(listener,fiter)则ExtensionLoader.getExtension()会将真正的实现类经过Wrapper(listener,fiter)包装后返回。

 

Dubbo扩展点AOP功能

对dubbo扩展点作切面功能的扩展,从ExtensionLoader的createExtension() 代码提及:

@SuppressWarnings("unchecked")
    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);
            }
            injectExtension(instance);


            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            //begin 
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            //end 
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

关键说明, 
1. cachedWrapperClasses是在loadFile里面加载的,”WrapperClass”是符合某种特征的扩展接口实现类的称呼。例如ProtocolFilterWrapper 
和ProtocolListenerWrapper。他们共同特征就是带有Protocol接口的构造函数。

/**
 * ListenerProtocol
 *
 * @author william.liangf
 */
public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }

    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
    }

    public int getDefaultPort() {
        return protocol.getDefaultPort();
    }

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

    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 buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }

    public void destroy() {
        protocol.destroy();
    }

}
  1. instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 就是将拿到的instance放到一个包装类中,而后通过一层包装以后,在放到另一个包装类中, 
    经过这种方式dubbo实现了扩展点的AOP

遇到的设计模式

  1. 装饰器模式
  2. 动态代理模式
相关文章
相关标签/搜索