dubbo源码分析二:SPI扩展

概述

哈喽,咱们如约而至,今天咱一起看下dubbo的SPI扩展部分,说起这个不禁感慨当初开发dubbo的人,脑子怎么长得做出来这么优秀的产品。

我们可以基于SPI做过滤器、负载均衡器、日志打印、协议扩展等等操作,非常的方便,而且相对于jdk原生SPI做了不少优化。

咱们先实战一波,看下具体是如何使用的,然后再分析下源码。

应用实战

dubbo支持的SPI扩展有:协议扩展、调用拦截扩展、引用监听扩展、暴露监听扩展、集群扩展、路由扩展、负载均衡扩展、合并结果扩展、注册中心扩展、监控中心扩展、扩展点加载扩展、动态代理扩展、编译器扩展、配置中心扩展、消息派发扩展、线程池扩展、序列化扩展、网络传输扩展、信息交换扩展、组网扩展、Telnet 命令扩展、状态检查扩展、容器扩展、缓存扩展、验证扩展、日志适配扩展 等等。

咱们今天实现定义日志过滤器,记录rpc执行耗时,首先在消费者项目中创建LogMonitorFilter类实现Filter,代码如下:

public class LogMonitorFilter implements Filter {
   @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Long startTime=System.currentTimeMillis();
        Result invoke = invoker.invoke(invocation);
        Long endTime=System.currentTimeMillis();
        System.out.println(String.format("----------------------------%s类的%s方法rpc耗时:%s",invocation.getServiceName(),invocation.getMethodName(),(endTime-startTime)));
        return invoke;
    }
}

创建 META-INF.dubbo.internal/org.apache.dubbo.rpc.Filter

文件内容如下:log=org.apache.dubbo.demo.consumer.comp.LogMonitorFilter

在rpc接口上通过注解指定用那个filter:

@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {

    @DubboReference(interfaceClass = org.apache.dubbo.demo.DemoService.class,filter = "log")
    private DemoService demoService;
    @Override
    public String sayHello(String name) {
        return demoService.sayHello(name);
    }

    @Override
    public CompletableFuture<String> sayHelloAsync(String name) {
        return null;
    }
}
 

运行程序在控制台完美打印了我们的过滤器:

接下来咱们从源码层面分析下它是如何加载我们的SPI过滤器的

源码分析

先看下包结构:

org.apache.dubbo.common.extension包下的都是关于SPI的实现,重点是ExtensionLoader这个类。

这个类有近1200行的代码,看起来不是很方便,所以我们还是采用debug的模式一步步的跟踪。

咱们先创建3个类(Car、Bmw、Test),Car为接口 Bmw为具体实现通过test做测试,并在META-INF下创建文件如图:

Car代码如下:

@SPI
public interface Car {

    void toHome();
}
 

Bmw代码如下:​​​​​​​

public class Bmw implements Car{
    @Override
    public void toHome() {
        System.out.println("开着宝马回老家!");
    }
}
 

Test类代码:

public class Test {

    public static void main(String[] args) {
        Car bmw = ExtensionLoader.getExtensionLoader(Car.class).getExtension("bmw");
        bmw.toHome();
    }
}

直接在getExtension方法设置条件断点到了这里:

public T getExtension(String name, boolean wrap) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        String cacheKey = name;
        if (!wrap) {
            cacheKey += "_origin";
        }
        final Holder<Object> holder = getOrCreateHolder(cacheKey);
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name, wrap);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
  

此时是没有实例化Bmw的如图:

然后进过后再看instance已经有了Bmw的对象实例了:

说明Bmw对象是由dubbo为我们创建的,可见dubbo实现了IOC的功能,而且是懒加载模式的,走完断点顺利开车回家了。

经过刚才调试我们知道了,重点在instance= createExtension(name, wrap);这行代码,再次断点进去看看具体实现方式,我们看下这个方法中的代码:

private T createExtension(String name, boolean wrap) {
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
            T instance = (T) extensionInstances.get(clazz);
            if (instance == null) {
                extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
                instance = (T) extensionInstances.get(clazz);
                //看到这里这方法是不是很熟悉
                instance = postProcessBeforeInitialization(instance, name);
                injectExtension(instance);
                instance = postProcessAfterInitialization(instance, name);
            }

            if (wrap) {
                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                            || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                            instance = postProcessAfterInitialization(instance, name);
                        }
                    }
                }
            }

            // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }
 

细心的这里已经发现了熟悉的地方postProcessBeforeInitialization、postProcessAfterInitialization 这是典型的spring的后置处理器的思想,通过这个来干扰bean的实例化过程,dubbo这ioc照搬的spring的思想啊这是,不信咱们看下这个类更为熟悉的东西:

不禁感叹dubbo yyds!继续写一步果然还是空的,接下来就要实例化我们的类了:

咱们看下实例化对象的代码:​​​​​​​

public <T> T instantiate(Class<T> type) throws ReflectiveOperationException {

        // should not use default constructor directly, maybe also has another constructor matched scope model arguments
        // 1. try get default constructor
        Constructor<T> defaultConstructor = null;
        try {
            defaultConstructor = type.getConstructor();
        } catch (NoSuchMethodException e) {
            // ignore no default constructor
        }

        // 2. use matched constructor if found
        List<Constructor> matchedConstructors = new ArrayList<>();
        Constructor<?>[] declaredConstructors = type.getConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
            if (isMatched(constructor)) {
                matchedConstructors.add(constructor);
            }
        }
        // remove default constructor from matchedConstructors
        if (defaultConstructor != null) {
            matchedConstructors.remove(defaultConstructor);
        }

        // match order:
        // 1. the only matched constructor with parameters
        // 2. default constructor if absent

        Constructor targetConstructor;
        if (matchedConstructors.size() > 1) {
            throw new IllegalArgumentException("Expect only one but found " +
                matchedConstructors.size() + " matched constructors for type: " + type.getName() +
                ", matched constructors: " + matchedConstructors);
        } else if (matchedConstructors.size() == 1) {
            targetConstructor = matchedConstructors.get(0);
        } else if (defaultConstructor != null) {
            targetConstructor = defaultConstructor;
        } else {
            throw new IllegalArgumentException("None matched constructor was found for type: " + type.getName());
        }

        // create instance with arguments
        Class[] parameterTypes = targetConstructor.getParameterTypes();
        Object[] args = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            args[i] = getArgumentValueForType(parameterTypes[i]);
        }
        //通过构造方法反射获取对象
        return (T) targetConstructor.newInstance(args);
    }
 

通过构造方法反射获取对象,在回过来看​​​​​​​

instance = postProcessBeforeInitialization(instance, name);instance = postProcessAfterInitialization(instance, name);

这2行代码,实际跟下去发现postProcessBeforeInitialization并没有实际做什么,postProcessAfterInitialization中对bean的作用于做了一下处理,到此我们有发现一个扩展项就是ExtensionPostProcessor可以通过它来干扰bean。

那它怎么获取的bmw的Class的呢?关键代码在createExtension Class<?> clazz = getExtensionClasses().get(name);一路跟踪来到如下代码处:

    private Map<String, Class<?>> loadExtensionClasses() {
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();
        //通过jdk的spi加载3个dubbo的LoadingStrategy  
        for (LoadingStrategy strategy : strategies) {
            loadDirectory(extensionClasses, strategy, type.getName());

            // compatible with old ExtensionFactory
            if (this.type == ExtensionInjector.class) {
                loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
            }
        }

        return extensionClasses;
    }

    private void loadDirectory(Map<String, Class<?>> extensionClasses, LoadingStrategy strategy, String type) {
        loadDirectory(extensionClasses, strategy.directory(), type, strategy.preferExtensionClassLoader(),
            strategy.overridden(), strategy.excludedPackages(), strategy.onlyExtensionClassLoaderPackages());
        String oldType = type.replace("org.apache", "com.alibaba");
        loadDirectory(extensionClasses, strategy.directory(), oldType, strategy.preferExtensionClassLoader(),
            strategy.overridden(), strategy.excludedPackages(), strategy.onlyExtensionClassLoaderPackages());
    }
 

这里处理我们的类就在循环strategies的时候,这个是dubbo的三个默认处理SPI的类如图:

这个三个类源码如下:

public class DubboInternalLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/dubbo/internal/";
    }

    @Override
    public int getPriority() {
        return MAX_PRIORITY;
    }
}
public class DubboLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/dubbo/";
    }

    @Override
    public boolean overridden() {
        return true;
    }

    @Override
    public int getPriority() {
        return NORMAL_PRIORITY;
    }


}

public class ServicesLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/services/";
    }

    @Override
    public boolean overridden() {
        return true;
    }

    @Override
    public int getPriority() {
        return MIN_PRIORITY;
    }
}
 

可以清楚的看到就是这3个类加载META-INF下的dubbo扩展的,这3个类是由JDK的SPI来加载的,代码如下:

 /**
     * Load all {@link Prioritized prioritized} {@link LoadingStrategy Loading Strategies} via {@link ServiceLoader}
     * 通过此处从jdk实例化的接口中拿出实例
     * @return non-null
     * @since 2.7.7
     */
    private static LoadingStrategy[] loadLoadingStrategies() {
        return stream(load(LoadingStrategy.class).spliterator(), false)
            .sorted()
            .toArray(LoadingStrategy[]::new);
    }
  

继续debug来到了如下方法:

这个方法通过获取classLoader来加载META-INF下的数据,继续往下:

  private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
                              java.net.URL resourceURL, boolean overridden, String[] excludedPackages, String[] onlyExtensionClassLoaderPackages) {
        try {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                String line;
                String clazz = null;
                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();
                                clazz = line.substring(i + 1).trim();
                            } else {
                                clazz = line;
                            }
                            if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)
                                && !isExcludedByClassLoader(clazz, classLoader, onlyExtensionClassLoaderPackages)) {
                                loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type +
                                ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                            exceptions.put(line, e);
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }
 

看到真实面幕了通过 Class.forName(clazz, true, classLoader)获取得到真实实现类的class,到了这里我们的思路也清洗了。


首先通过JDK的SPI来实例化dubbo自己的处理类,然后通过懒加载的方式去实例化具体的SPI扩展对象,实例化的时候先通过DubboInternalLoadingStrategy扫描指定路径下的SPI文件,或得到class再通过反射得到整个bean对象,下次直接从ioc容器中获取对象实例即可。

咱们再看实际运行中是在哪里执行的filter,是通过

StubProxyFactoryWrapper--->export--->  

        ProtocolFilterWrapper---->export--->   

            DefaultFilterChainBuilder ----> buildInvokerChain

buildInvokerChain方法源码如下:​​​​​​​

  @Override
    public <T> Invoker<T> buildInvokerChain(final Invoker<T> originalInvoker, String key, String group) {
        Invoker<T> last = originalInvoker;
        URL url = originalInvoker.getUrl();
        //获取filter
        List<Filter> filters = ScopeModelUtil.getExtensionLoader(Filter.class, url.getScopeModel()).getActivateExtension(url, 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 FilterChainNode<>(originalInvoker, next, filter);
            }
        }

        return last;
    }
 

参照dubbo的十层架构图看起来会更加清晰:

总结

咱们能够基于 Dubbo 提供的扩展能力,很方便基于自身需求扩展其他协议、过滤器、路由等。

  • 按需加载。Dubbo 的扩展能力不会一次性实例化所有实现,而是用那个扩展类则实例化那个扩展类,减少资源浪费。

  • 增加扩展类的 IOC 能力。Dubbo 的扩展能力并不仅仅只是发现扩展服务实现类,而是在此基础上更进一步,如果该扩展类的属性依赖其他对象,则 Dubbo 会自动的完成该依赖对象的注入功能。

  • 增加扩展类的 AOP 能力。Dubbo 扩展能力会自动的发现扩展类的包装类,完成包装类的构造,增强扩展类的功能。

  • 具备动态选择扩展实现的能力。Dubbo 扩展会基于参数,在运行时动态选择对应的扩展类,提高了 Dubbo 的扩展能力。

  • 可以对扩展实现进行排序。能够基于用户需求,指定扩展实现的执行顺序。

  • 提供扩展点的 Adaptive 能力。该能力可以使的一些扩展类在 consumer 端生效,一些扩展类在 provider 端生效。

dubbo作者果然牛逼IOC、AOP的思想人家玩的溜溜的,源码中还有不少处用了弱引用等作为cache都非常值得我们借鉴,好了这期就到这了咱们下期见!

MYSQL系列经典文章


​​​​​​​

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值