【Dubbo系列】插件化

Jdk的SPI

SPI全称是Service Provider Interface,是JDK内置的一种服务提供发现机制。java.util.ServiceLoader提供了简单实现,以下是该实现的相关描述:

  1. 服务定义 如:com.example.CodecSet
  2. 服务实现 如:com.example.impl.StandardCodecs
  3. 服务配置文件
    • 目录:META-INF/services
    • 文件名:服务接口类的全名,如:META-INF/services/com.example.CodecSet
    • 文件内容:服务实现类的全名,每行一个,如:com.example.impl.StandardCodecs
  4. 服务加载器 加载配置文件中的所有实现类
// JDK内部实现的服务发现类 提供懒加载机制
// 配置文件逐个懒加载,单个文件内的单行也逐个懒加载
private class LazyIterator implements Iterator<S>
{
    Enumeration<URL> configs;// 所有配置文件路径
    Iterator<String> pending;// 当前处理中的文件的内容,每行一个值
    ......
    private boolean hasNextService() {
        if (configs == null) {
            try {
                // 获得该服务的所有 提供者配置文件路径
                String fullName = PREFIX + service.getName();
                if (loader == null)
                    configs = ClassLoader.getSystemResources(fullName);
                else
                    configs = loader.getResources(fullName);
            } catch (IOException x) {
                fail(service, "Error locating configuration files", x);
            }
        }
        while ((pending == null) || !pending.hasNext()) {
            if (!configs.hasMoreElements()) {
                return false;
            }
            // 读取下一个配置文件
            pending = parse(service, configs.nextElement());
        }
        // 读取配置文件中的一行
        nextName = pending.next();
        return true;
    }

    private S nextService() {
        if (!hasNextService()) throw new NoSuchElementException();
        String cn = nextName;
        nextName = null;
        try {
            // 获取实现类
            c = Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
            fail(service, "Provider " + cn + " not found");
        }
        try {
            // 获取一个对象,并缓存
            S p = service.cast(c.newInstance());
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            fail(service, "Provider " + cn + " could not be instantiated", x);
        }
        throw new Error();          // This cannot happen
    }
    ......
}

private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
private LazyIterator lookupIterator;
// 对外提供的迭代器,遍历所有的实现类
public Iterator<S> iterator() {
    return new Iterator<S>() {
        Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();

        public boolean hasNext() {
            if (knownProviders.hasNext()) return true;
            return lookupIterator.hasNext();
        }

        public S next() {
            // 读取缓存中的 已加载的实现类
            if (knownProviders.hasNext())
                return knownProviders.next().getValue();
            // 已加载的实现类读取完后,从懒加载器中加载
            return lookupIterator.next();
        }
    };
}

可见Jdk的插件化实现特别简单,约定 配置文件的格式,然后实现一个ServiceLoader通过配置文件加载相关实现类,并通过迭代器全部暴露出去。下面看下Dubbo的相关实现

Dubbo的SPI

Dubbo实现了一套更灵活的SPI插件化机制,首先配置文件中增加服务别名,另外新增注解@SPI, @Adaptive, @Activate, 决定激活哪个实现。@SPI指定默认激活的服务别名;@Adaptive是适配器注解,其值作为key从入参中取值即为服务别名;@Activate是激活类注解,其值作为key判定入参中是否存在,若存在就激活,常用于过滤器,指定哪些filter要执行。另外Dubbo还有Wrapper装箱类,在获取具体实现类时默认做封装操作

加载器

涉及到的缓存对象:

// 类型
private final Class<?> type;
// 扩展类-->别名
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
// 别名-->扩展类
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
// 别名-->类上的@Activate注解
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<String, Object>();
// 缓存适配器类
private volatile Class<?> cachedAdaptiveClass = null;
// 默认别名
private String cachedDefaultName;
// 缓存包装类,自动装箱时,对原对象进行封装
private Set<Class<?>> cachedWrapperClasses;

加载所有扩展并缓存的服务:

// synchronized in getExtensionClasses
// 加载所有扩展并返回
private Map<String, Class<?>> loadExtensionClasses() {
    // @SPI指定默认取值
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation != null) {
        String value = defaultAnnotation.value();
        if ((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];
        }
    }

    // 依次从三类目录中读取对应服务的配置文件
    // repalce是为了兼容原有包名为com.alibaba的旧服务
    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    return extensionClasses;
}

// 从配置目录中 找到对应的配置文件,并读取
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
    String fileName = dir + type;
    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 resourceURL = urls.nextElement();
                loadResource(extensionClasses, classLoader, resourceURL);
            }
        }
    } catch (Throwable t) {
        logger.error("Exception when load extension class(interface: " +
                type + ", description file: " + fileName + ").", t);
    }
}

// 从具体文件中读取所有扩展
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
    try {
        BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
        try {
            String line;
            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) {
                            // 逐行读取,并加载相关实现类
                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                        }
                    } catch (Throwable t) {
                    }
                }
            }
        } finally {
            reader.close();
        }
    } catch (Throwable t) {
    }
}

// extensionClasses-扩展类缓存
// resourceURL-配置文件地址
// clazz-服务实现类
// name-服务别名
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException("Error when load extension class(interface: " + ...);
    }
    // 适配器类:不能多于1个
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        if (cachedAdaptiveClass == null) {
            cachedAdaptiveClass = clazz;
        } else if (!cachedAdaptiveClass.equals(clazz)) {
            throw new IllegalStateException("More than 1 adaptive class found: " + ...);
        }
    // 包装类直接放入缓存
    } else if (isWrapperClass(clazz)) {
        cachedWrapperClasses.add(clazz);
    // 其余的都作为普通实现类
    } else {
        clazz.getConstructor();
        if (name == null || name.length() == 0) {
            // 别名默认从类名中取
            name = findAnnotationName(clazz);
        }
        String[] names = NAME_SEPARATOR.split(name);
        if (names != null && names.length > 0) {
            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) {
                // 缓存类级别的激活注解
                cachedActivates.put(names[0], activate);
            } else {
                // support com.alibaba.dubbo.common.extension.Activate
                ...
            }
            for (String n : names) {
                // 缓存别名
                if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, n); }
                // 缓存扩展
                Class<?> c = extensionClasses.get(n);
                if (c == null) {
                    extensionClasses.put(n, clazz);
                } else if (c != clazz) {
                    // 别名重复抛异常
                }
            }
        }
    }
}

这里的加载器功能,将所有配置文件中的扩展类加载并缓存起来,分为适配器类、包装类、普通实现类。

扩展注入

// 扩展实例缓存
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
// 别名-->依赖注入、自动装箱后的实例
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
// 适配器对象
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();

// 指定别名查找对应的扩展类
@SuppressWarnings("unchecked")
public T getExtension(String name) {
    if (name == null || name.length() == 0)
        throw new IllegalArgumentException("Extension name == null");
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(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<?> 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);
        }
        // 依赖注入
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            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);
    }
}

private T injectExtension(T instance) {
    try {
        if (objectFactory != null) {
            for (Method method : instance.getClass().getMethods()) {
                // 处理所有set方法
                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);
                        }
                    } catch (Exception e) {
                        logger.error("fail to inject via method " + method.getName()
                                + " of interface " + type.getName() + ": " + e.getMessage(), e);
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

这里在第一次获取实例时,会做一个依赖注入、自动装箱操作。依赖注入简单来说就是从对象工厂取对象出来,放入实例的set方法中;自动装箱就是使用装箱类的又参数构造器,构造包含实例的装箱类。

对象工厂

private final ExtensionFactory objectFactory = 
        (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
// 对象工厂的适配器类
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            // 依次从工厂类中取实例
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

// SPI对象工厂
public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    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().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}

// Spring对象工厂
public class SpringExtensionFactory implements ExtensionFactory {
    ......
}

// 配置文件
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory
// 这个在dubbo-config-spring包下配置
spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory

对象工厂本身也是基于插件机制实现的,自定义了适配器类、spi、spring扩展实现类。适配器类依次从扩展实现类中取相应对象,取出来就返回。

@Activate

用于指定所有实现类中,哪些被激活。以下为用法描述:

/**
 * 依据给定的标准 激活相应的扩展, 例如: 用于有多个实现类的Filter接口上,确定哪些Filter需要激活。
 *   Activate.group属性指定分组标准,指定分组的调用才激活,SPI框架确定有效的分组值。
 *   Activate.value属性指定入参URL中的参数key,入参中存在该参数才激活。
 *   通过调用ExtensionLoader#getActivateExtension(URL, String, String)}获得所有给定标准下激活的扩展
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    String[] group() default {};
    String[] value() default {};
    @Deprecated
    String[] before() default {};
    @Deprecated
    String[] after() default {};
    // 绝对的执行顺序信息,值越大越靠前
    int order() default 0;
}

// 在消费端和服务提供端都激活
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
public class MonitorFilter implements Filter {......}

// 在消费端和服务提供端,入参包含CACHE_KEY时激活
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)
public class CacheFilter implements Filter {......}

// 在消费端和服务提供端,入参包含VALIDATION_KEY时激活
// order=10000 指定顺序,值越大越靠前
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.VALIDATION_KEY, order = 10000)
public class ValidationFilter implements Filter {......}

以下为源码剖析 ExtensionLoader.java:

    public List<T> getActivateExtension(URL url, String key, String group) {
        String value = url.getParameter(key);
        return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
    }
    // values-自定义的扩展的别名
    public List<T> getActivateExtension(URL url, String[] values, String group) {
        List<T> exts = new ArrayList<T>();
        List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
        // 加载默认扩展类
        if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
            getExtensionClasses();// 加载所有配置文件中的扩展
            for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
                String name = entry.getKey();
                Object activate = entry.getValue();

                String[] activateGroup, activateValue;
                if (activate instanceof Activate) {
                    activateGroup = ((Activate) activate).group();
                    activateValue = ((Activate) activate).value();
                } else {
                    continue;
                }
                // group匹配:gourp为null时默认激活,否则判断group是否在activateGroup列表中
                if (isMatchGroup(group, activateGroup)) {
                    T ext = getExtension(name);
                    // 非指定扩展
                    if (!names.contains(name) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
                            // value匹配
                            && isActive(activateValue, url)) {
                        exts.add(ext);
                    }
                }
            }
            // 按order排序,before after已废弃
            Collections.sort(exts, ActivateComparator.COMPARATOR);
        }
        // 加载用户自定义的扩展类
        List<T> usrs = new ArrayList<T>();
        for (int i = 0; i < names.size(); i++) {
            String name = names.get(i);
            if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
                // 若指定了默认扩展,则将default前面的扩展都作为默认扩展,放在扩展列表的头部
                if (Constants.DEFAULT_KEY.equals(name)) {
                    if (!usrs.isEmpty()) {
                        exts.addAll(0, usrs);
                        usrs.clear();
                    }
                } else {
                    T ext = getExtension(name);
                    usrs.add(ext);
                }
            }
        }
        // 用户自定义的扩展类,放在后面
        if (!usrs.isEmpty()) {
            exts.addAll(usrs);
        }
        return exts;
    }
    
    // 判断是否需要激活:url中存在参数keys中的一个
    private boolean isActive(String[] keys, URL url) {
        if (keys.length == 0) {
            return true;
        }
        for (String key : keys) {
            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
                String k = entry.getKey();
                String v = entry.getValue();
                if ((k.equals(key) || k.endsWith("." + key))
                        && ConfigUtils.isNotEmpty(v)) {
                    return true;
                }
            }
        }
        return false;
    }

Dubbo内的相关使用类 ProtocolFilterWrapper.java:

    private final 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.isEmpty()) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {
                    ......
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }
                    ......
                };
            }
        }
        return last;
    }

    @Override
    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));
    }

    @Override
    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);
    }

@Adaptive

适配器类,可以自定义实现,也可以依赖插件机制的自动生成。 下面看下扩展机制的适配器自动生成实现,这个方法太重了,其实可以拆一下。实现的功能是从入参中取扩展别名,然后调用该扩展的实现类,达到通过入参自动调用不同实现的功能。


    private String createAdaptiveExtensionClassCode() {
        StringBuilder codeBuilder = new StringBuilder();
        Method[] methods = type.getMethods();
        // 必须有待适配的method
        boolean hasAdaptiveAnnotation = false;
        for (Method m : methods) {
            if (m.isAnnotationPresent(Adaptive.class)) {
                hasAdaptiveAnnotation = true;
                break;
            }
        }
        if (!hasAdaptiveAnnotation)
            throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");

        codeBuilder.append("package ").append(type.getPackage().getName()).append(";");
        codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";");
        codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");

        codeBuilder.append("\nprivate static final org.apache.dubbo.common.logger.Logger logger = org.apache.dubbo.common.logger.LoggerFactory.getLogger(ExtensionLoader.class);");
        codeBuilder.append("\nprivate java.util.concurrent.atomic.AtomicInteger count = new java.util.concurrent.atomic.AtomicInteger(0);\n");

        // 逐个方法生成代理
        for (Method method : methods) {
            Class<?> rt = method.getReturnType();
            Class<?>[] pts = method.getParameterTypes();
            Class<?>[] ets = method.getExceptionTypes();

            Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
            StringBuilder code = new StringBuilder(512);
            // 以下为方法体
            if (adaptiveAnnotation == null) {
                // 方法没有注解时不处理
                code.append("throw new UnsupportedOperationException(\"method ")
                        .append(method.toString()).append(" of interface ")
                        .append(type.getName()).append(" is not adaptive method!\");");
            } else {
                int urlTypeIndex = -1;
                for (int i = 0; i < pts.length; ++i) {
                    if (pts[i].equals(URL.class)) {
                        urlTypeIndex = i;
                        break;
                    }
                }
                // 找到URL参数
                if (urlTypeIndex != -1) {
                    // Null Point check
                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",
                            urlTypeIndex);
                    code.append(s);

                    s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);
                    code.append(s);
                }
                // 若没有URL参数,从每个入参的get方法中读取
                else {
                    String attribMethod = null;

                    // find URL getter method
                    LBL_PTS:
                    for (int i = 0; i < pts.length; ++i) {
                        Method[] ms = pts[i].getMethods();
                        for (Method m : ms) {
                            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) {
                                urlTypeIndex = i;
                                attribMethod = name;
                                break LBL_PTS;
                            }
                        }
                    }
                    if (attribMethod == null) {
                        throw new IllegalStateException("fail to create adaptive class for interface " + type.getName()
                                + ": not found url parameter or url attribute in parameters of method " + method.getName());
                    }

                    // Null point check
                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",
                            urlTypeIndex, pts[urlTypeIndex].getName());
                    code.append(s);
                    s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",
                            urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);
                    code.append(s);

                    s = String.format("%s url = arg%d.%s();", URL.class.getName(), urlTypeIndex, attribMethod);
                    code.append(s);
                }

                String[] value = adaptiveAnnotation.value();
                // 注解的value 未设置时,从类名中取
                if (value.length == 0) {
                    char[] charArray = type.getSimpleName().toCharArray();
                    StringBuilder sb = new StringBuilder(128);
                    for (int i = 0; i < charArray.length; i++) {
                        if (Character.isUpperCase(charArray[i])) {
                            if (i != 0) {
                                sb.append(".");
                            }
                            sb.append(Character.toLowerCase(charArray[i]));
                        } else {
                            sb.append(charArray[i]);
                        }
                    }
                    value = new String[]{sb.toString()};
                }

                boolean hasInvocation = false;
                for (int i = 0; i < pts.length; ++i) {
                    if (pts[i].getName().equals("org.apache.dubbo.rpc.Invocation")) {
                        // Null Point check
                        String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i);
                        code.append(s);
                        // 入参中包含Invocation时,从里面取出methodName,再按照methodName从URL里取参数值
                        s = String.format("\nString methodName = arg%d.getMethodName();", i);
                        code.append(s);
                        hasInvocation = true;
                        break;
                    }
                }

                // 取出入参中指定的别名
                String defaultExtName = cachedDefaultName;
                String getNameCode = null;
                for (int i = value.length - 1; i >= 0; --i) {
                    if (i == value.length - 1) {
                        if (null != defaultExtName) {
                            if (!"protocol".equals(value[i]))
                                if (hasInvocation)
                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                                else
                                    getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
                            else
                                getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
                        } else {
                            if (!"protocol".equals(value[i]))
                                if (hasInvocation)
                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                                else
                                    getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
                            else
                                getNameCode = "url.getProtocol()";
                        }
                    } else {
                        if (!"protocol".equals(value[i]))
                            if (hasInvocation)
                                getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                            else
                                getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
                        else
                            getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
                    }
                }
                code.append("\nString extName = ").append(getNameCode).append(";");
                // 未指定别名时 抛异常
                String s = String.format("\nif(extName == null) " +
                                "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",
                        type.getName(), Arrays.toString(value));
                code.append(s);

                // 按照别名,取对应的扩展
                code.append(String.format("\n%s extension = null;\n try {\nextension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);\n}catch(Exception e){\n",
                        type.getName(), ExtensionLoader.class.getSimpleName(), type.getName()));
                code.append(String.format("if (count.incrementAndGet() == 1) {\nlogger.warn(\"Failed to find extension named \" + extName + \" for type %s, will use default extension %s instead.\", e);\n}\n",
                        type.getName(), defaultExtName));
                // 异常时,按默认别名取扩展
                code.append(String.format("extension = (%s)%s.getExtensionLoader(%s.class).getExtension(\"%s\");\n}",
                        type.getName(), ExtensionLoader.class.getSimpleName(), type.getName(), defaultExtName));

                // 调用对应的方法,并返回
                if (!rt.equals(void.class)) {
                    code.append("\nreturn ");
                }

                s = String.format("extension.%s(", method.getName());
                code.append(s);
                for (int i = 0; i < pts.length; i++) {
                    if (i != 0)
                        code.append(", ");
                    code.append("arg").append(i);
                }
                code.append(");");
            }

            // 以下为方法声明
            codeBuilder.append("\npublic ").append(rt.getCanonicalName()).append(" ").append(method.getName()).append("(");
            for (int i = 0; i < pts.length; i++) {
                if (i > 0) {
                    codeBuilder.append(", ");
                }
                codeBuilder.append(pts[i].getCanonicalName());
                codeBuilder.append(" ");
                codeBuilder.append("arg").append(i);
            }
            codeBuilder.append(")");
            if (ets.length > 0) {
                codeBuilder.append(" throws ");
                for (int i = 0; i < ets.length; i++) {
                    if (i > 0) {
                        codeBuilder.append(", ");
                    }
                    codeBuilder.append(ets[i].getCanonicalName());
                }
            }
            codeBuilder.append(" {");
            codeBuilder.append(code.toString());
            codeBuilder.append("\n}");
        }
        codeBuilder.append("\n}");
        if (logger.isDebugEnabled()) {
            logger.debug(codeBuilder.toString());
        }
        return codeBuilder.toString();
    }

总结

JDK实现了最基本的SPI机制,通过约定从配置文件中读取所有扩展,实现了一套懒加载的迭代器读取扩展,而相关扩展的使用需要用户自己实现。Dubbo的扩展配置文件增加了别名,并通过@Adaptive、@Activate以及入参的配合,实现了灵活的扩展使用方法,用户只需通过入参就能决定激活哪些扩展,无需实现新的使用方法。

转载于:https://my.oschina.net/u/3026036/blog/2253984

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
gatling dubbo件是一个基于Gatling框架的非官方Dubbo压测件。该件可以用于在Dubbo和其他Java生态系统上运行负载测试。件基于Gatling 2.3.1版本开发,已经在Dubbo 2.6.5版本上进行了测试,但理论上适用于所有Dubbo版本。 件有两个版本,即1.0和2.0。1.0版本使用泛调用方式执行压测请求,而2.0版本采用普通API调用方式。建议使用2.0件,因为官方推荐在生产环境中使用普通API调用方式,这样得到的压测结果更具参考意义。而且,2.0件无需对dubbo框架进行任何改造。 使用gatling dubbo件,你可以通过编写模拟脚本来定义Dubbo接口的压测场景。这些脚本可以模拟并发用户对Dubbo接口的请求,并对接口的性能进行评估。根据提供的引用,一个名为DubboTest的模拟场景在31秒内完成了。具体使用方法和配置可以参考件的文档和示例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Gatling的非官方Dubbo压测件,基于Gatling 2.3.1。](https://blog.csdn.net/weixin_39774490/article/details/111843354)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [gatling-dubbo:一个加特林件,用于在Apache Dubbo(https:github.comapacheincubator-dubbo)和其他...](https://download.csdn.net/download/weixin_42168341/15092472)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值