dubbo spi 原理解析

一、spi 使用

创建spi 接口及对应实现

package apache.dubbo.test.spi;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface SpiInterface {
    void method();
}


public class SpiInterfaceImpl implements SpiInterface {
    @Override
    public void method() {
        System.out.println(this.getClass().getName());
    }
}

 在META-INFO/dubbo中声明审批配置,创建与spi接口类名相同的文件apache.dubbo.test.spi.SpiInterface,在文档内编辑

my=apache.dubbo.test.spi.SpiInterfaceImpl

使用 SPI

ExtensionLoader<SpiInterface> extensionLoader = ExtensionLoader.getExtensionLoader(SpiInterface.class);
        SpiInterface spiInstance = extensionLoader.getExtension("my");
        spiInstance.method();

二、原理解析

public class ExtensionLoader<T> {
    //loader 缓存
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        // ...
        return (ExtensionLoader<T>) EXTENSION_LOADERS.computeIfAbsent(type, k -> new ExtensionLoader<T>(type));
    }

    // 获取实例
    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        // 注解中配置的默认实现
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        // 从缓存获取
        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);
                }
            }
        }
        return (T) instance;
    }
    
    // 创建实例
    private T createExtension(String name) {
        //获取class对象
        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, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //注入set方法
            injectExtension(instance);
            //包装类
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            //如果有包装类,对对象进行包装
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            //执行init
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

    // 获取审批配置
    /**
     * 获取全量的class配置
     * @return
     */
    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    // 解析META-INFO下的配置文件
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

    /**
     * synchronized in getExtensionClasses
     * */
    private Map<String, Class<?>> loadExtensionClasses() {
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();
        // 从对应文件路径下加载文件 [META-INF/services/,META-INF/dubbo/,internal/]
        for (LoadingStrategy strategy : strategies) {
            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
        }

        return extensionClasses;
    }
}

三、Adaptive

Dubbo Spi 提供获取适配对象的代理适配器的api,dubbo中大量使用了该方法,实现动态的调用

ExtensionLoader<SpiInterface> extensionLoader = ExtensionLoader.getExtensionLoader(SpiInterface.class);
        SpiInterface spiInstance = extensionLoader.getAdaptiveExtension();

动态适配器对象有两种创建方式

1 Adaptive 类,用户自定义

    用户创建一个类继承SPI接口,添加@Adaptive注解,用户自己实现对象的配逻辑

@Adaptive
public class SpiIoc implements SpiInterface {
    @Override
    public void method(URL url) {
        // 适配逻辑
    }
}

2 Adaptive方法,dubbo自动创建

SPI接口方法被 @Adaptive标记,参数列表总必须带有org.apache.dubbo.common.URL,或者参数对象里有对象包含获取URL对象的get方法,针对这些方法,dubbo会为其创建代理方法

@SPI
public interface SpiInterface {
    @Adaptive("my") // 如果注解value是protocol,通过url的协议名称进行适配;否则通过url同名的parameter适配
    void method(URL url);
}

 

源码实现

public class ExtensionLoader<T> {
    // ...
    //nb  该方法是实际生成的是一个代理适配器,根据spi 类型去调用对应的类
    public T getAdaptiveExtension() {
        // 缓存
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError != null) {
                throw new IllegalStateException("Failed to create adaptive instance: " +
                        createAdaptiveInstanceError.toString(),
                        createAdaptiveInstanceError);
            }

            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        // 创建
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        }

        return (T) instance;
    }

    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        // 有Adaptive类 在之前加载SPI配置文件时已经初始化
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        // 通过javassist自动创建代理类
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

    private Class<?> createAdaptiveExtensionClass() {
        // 生成代理类的代码
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

}

下面是代码生成器的源码 

public class AdaptiveClassCodeGenerator {

    /**
     * 生成代码
     * generate and return class code
     */
    public String generate() {
        // no need to generate adaptive class since there's no adaptive method found.
        // 必须带有@Adaptive的方法
        if (!hasAdaptiveMethod()) {
            throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
        }

        StringBuilder code = new StringBuilder();
        code.append(generatePackageInfo());
        code.append(generateImports());
        code.append(generateClassDeclaration());
        // 组装代理方法
        Method[] methods = type.getMethods();
        for (Method method : methods) {
            code.append(generateMethod(method));
        }
        code.append("}");

        if (logger.isDebugEnabled()) {
            logger.debug(code.toString());
        }
        return code.toString();
    }

     /**
     * 创建代理方法的代码
     * generate method declaration
     */
    private String generateMethod(Method method) {
        String methodReturnType = method.getReturnType().getCanonicalName();
        String methodName = method.getName();
        // 方法内容
        String methodContent = generateMethodContent(method);
        String methodArgs = generateMethodArguments(method);
        String methodThrows = generateMethodThrows(method);
        return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
    }

    /**
     * 生成代理方法
     * generate method content
     */
    private String generateMethodContent(Method method) {
        Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
        StringBuilder code = new StringBuilder(512);
        // 非Adaptive方法,代理方法直接抛异常
        if (adaptiveAnnotation == null) {
            return generateUnsupported(method);
        } else {
            // 查找方法参数里的 URL
            int urlTypeIndex = getUrlTypeIndex(method);
            // found parameter in URL type
            if (urlTypeIndex != -1) {
                // Null Point check
                code.append(generateUrlNullCheck(urlTypeIndex));
            } else {
                // did not find parameter in URL type
                // 查找参数里的对象包含的 URL
                code.append(generateUrlAssignmentIndirectly(method));
            }

            //方法中@Adaptive 的value
            String[] value = getMethodAdaptiveValue(adaptiveAnnotation);

            boolean hasInvocation = hasInvocationArgument(method);
            // 组装org.apache.dubbo.rpc.Invocation 参数中指定的method
            code.append(generateInvocationArgumentNullCheck(method));
            // 组装要代理的Extension的ExtName,看是去url的parameter还是Protocol
            // value=protocol代表取url的protocol名称,其他值代表取url中相对应的parameter的值
            code.append(generateExtNameAssignment(value, hasInvocation));
            // check extName == null?
            code.append(generateExtNameNullCheck(value));
            // 组装通过SPI获取对应的Extension
            code.append(generateExtensionAssignment());

            // return statement
            code.append(generateReturnAndInvocation(method));
        }

        return code.toString();
    }
    
    // 没有从SPI接口方法里找到参数URL,遍历参数,从参数的下级去找URL
    private String generateUrlAssignmentIndirectly(Method method) {
        Class<?>[] pts = method.getParameterTypes();

        Map<String, Integer> getterReturnUrl = new HashMap<>();
        // find URL getter method
        for (int i = 0; i < pts.length; ++i) {
            for (Method m : pts[i].getMethods()) {
                String name = m.getName();
                if ((name.startsWith("get") || name.length() > 3)
                        && Modifier.isPublic(m.getModifiers())
                        && !Modifier.isStatic(m.getModifiers())
                        && m.getParameterTypes().length == 0
                        && m.getReturnType() == URL.class) {
                    getterReturnUrl.put(name, i);
                }
            }
        }

        if (getterReturnUrl.size() <= 0) {
            // getter method not found, throw
            throw new IllegalStateException("Failed to create adaptive class for interface " + type.getName()
                    + ": not found url parameter or url attribute in parameters of method " + method.getName());
        }

        Integer index = getterReturnUrl.get("getUrl");
        if (index != null) {
            return generateGetUrlNullCheck(index, pts[index], "getUrl");
        } else {
            Map.Entry<String, Integer> entry = getterReturnUrl.entrySet().iterator().next();
            return generateGetUrlNullCheck(entry.getValue(), pts[entry.getValue()], entry.getKey());
        }
    }

}

最终dubbo找到生成的代码如下,实际是返回了一个代理对象,通过接口参数里的URL中的信息对SPI进行适配

package apache.dubbo.test.spi;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class SpiInterface$Adaptive2 implements apache.dubbo.test.spi.SpiInterface {
    public void method(org.apache.dubbo.common.URL arg0)  {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        String extName = url.getParameter("my");
        if(extName == null) throw new IllegalStateException("Failed to get extension (apache.dubbo.test.spi.SpiInterface) name from url (" + url.toString() + ") use keys([my])");
        apache.dubbo.test.spi.SpiInterface extension = (apache.dubbo.test.spi.SpiInterface)ExtensionLoader.getExtensionLoader(apache.dubbo.test.spi.SpiInterface.class).getExtension(extName);
        extension.method(arg0);
    }
}

具体适配逻辑由于接口参数不同或者注解Adaptive的value不同实现稍有区别,但核心都是通过url的参数或者协议指定extName,在通过SPI返回对应的Extension

四、自动包装

    之前提到在创建实例时会有一个自动包装的过程

// 创建实例
    private T createExtension(String name) {
        //获取class对象
        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, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //注入set方法
            injectExtension(instance);
            //包装类
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            //如果有包装类,对对象进行包装
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            //执行init
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

如果有包装,是返回一个包装类的实例

包装类的判断通过检查SPI实现类是否包含参数是该SPI接口的构造函数,逻辑如下

private void cacheWrapperClass(Class<?> clazz) {
        if (cachedWrapperClasses == null) {
            cachedWrapperClasses = new ConcurrentHashSet<>();
        }
        cachedWrapperClasses.add(clazz);
    }

    /**
     * test if clazz is a wrapper class
     * <p>
     * which has Constructor with given class type as its only argument
     */
    private boolean isWrapperClass(Class<?> clazz) {
        try {
            //判断条件 带有参数的public构造器  且 构造器入参是需要包装的SPI接口
            clazz.getConstructor(type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

使用可以使用该功能实现AOP的操作

实现包装逻辑

public class SpiInterfaceWrapper implements SpiInterface {
    private SpiInterface spiInterface;

    public SpiInterfaceWrapper(SpiInterface spiInterface) {
        this.spiInterface = spiInterface;
    }

    @Override
    public void method(URL url) {
        System.out.println("wrapper do");
        spiInterface.method(url);
        System.out.println("wrapper after");
    }
}

在META-INFO配置中添加该SPI实现

my=apache.dubbo.test.spi.SpiInterfaceImpl
spiIoc=apache.dubbo.test.spi.SpiIoc
wrapper=apache.dubbo.test.spi.SpiInterfaceWrapper

这样最终获取到的SPI实例将会实现自动包装

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值