dubbo学习篇之dubbo内核解析(三)Wrapper

dubbo的内核

dubbo所有功能都是基于dubbo内核之上完成的,dubbo内核由四部分构成,分别为SPI,Adaptive,Wrapper,Activate。
而dubbo的内核设计原则,也是我们所熟悉的aop,ioc与动态编译compiler,这些称之为dubbo的内核原理。

Wrapper

Wrapper机制,即扩展点自动包装。Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。
扩展点的 Wrapper 类可以有多个,也可以根据需要新增。
通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。

Wrapper的规范

Wrapper 机制不是通过注解实现的,而是通过一套 Wrapper 规范实现的。
Wrapper 类在定义时需要遵循如下规范。

  • 该类要实现 SPI 接口
  • 该类中要有 SPI 接口的引用
  • 该类中必须含有一个含参的构造方法且参数只能有一个类型为SPI借口
  • 在接口实现方法中要调用 SPI 接口引用对象的相应方法
  • 该类名称以 Wrapper 结尾

Wrapper的代码实现

构建Wraaper类

package com.demo.adaptive.service.impl;

import com.demo.adaptive.service.Order;
import org.apache.dubbo.common.URL;

public class OrderWrapper implements Order {

    private Order order;

    // 这个构造方法是dubbo唯一判断该类是否是Wrapper类的依据
    public OrderWrapper(Order order) {
        this.order = order;
    }

    @Override
    public void pay(URL url) {
        System.out.println("before");
        order.pay(url);
        System.out.println("after");
    }
}

在服务提供者文件中定义Wrapper类

wrapper=com.demo.adaptive.service.impl.OrderWrapper

执行Main函数得到以下结果
在这里插入图片描述
如果需要多个Wrapper只需要定义多个符合规范的类,并填写在服务提供者文件上即可,但是需要注意,执行的顺序为后添加的先执行

Wrapper源码解析

Wrapper功能实现分为两个部分,一个是加载Extension时会把Wrapper类放入缓存中,另一部分取得服务提供者实例时,将装配过的Wrapper类返回。

加载Wrapper类

前面我们已经分析过了源码执行的流程,下面我只贴关键部分的代码

        // org.apache.dubbo.common.extension.ExtensionLoader 729-731
        // 判断该类是否是Wrapper类
        } else if (isWrapperClass(clazz)) {
            // 如果是Wrapper类,则经该类放入缓存
            cacheWrapperClass(clazz);
        }
    /**
     * 测试该类是否是一个Wrapper类
     * <p>
     * which has Constructor with given class type as its only argument
     */
    private boolean isWrapperClass(Class<?> clazz) {
        try {
            // 尝试取得参数类型为SPI接口类型的构造函数
            clazz.getConstructor(type);
            // 存在该类型的构造函数则为Wrapper类
            return true;
        } catch (NoSuchMethodException e) {
            // 不存在则不是Wrapper类
            return false;
        }
    }
    /**
     * 缓存Wrapper类
     * <p>
     * like: ProtocolFilterWrapper, ProtocolListenerWrapper
     */
    private void cacheWrapperClass(Class<?> clazz) {
        // 维护一个线程安全的HashSet来存放Wrapper
        // ExtensionLoader.getExtensionLoader(Order.class)
        // 通过上面取得ExtensionLoader的代码你需要知道,每一个SPI接口都有一个ExtensionLoader,所以这里面的缓存也是每一个SPI接口都有他的Wrapper缓存,生命周期和loader的生命周期一致
        if (cachedWrapperClasses == null) {
            cachedWrapperClasses = new ConcurrentHashSet<>();
        }
        cachedWrapperClasses.add(clazz);
    }

组装Wrapper类

组装Wrapper发生在取得SPI服务提供者实例时即下面代码执行时

 Order extension = (Order)ExtensionLoader.getExtensionLoader(Order.class).getExtension(extName);
    /**
     * 根据指定名字取得实例
     */
    @SuppressWarnings("unchecked")
    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;
    }
    @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, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            // 判断有无注入点
            // 这里和Spring IOC比较相似
            // 判断该类里面是否有set方法且该方法不是设置的基础参数类型
            // 并且该类得是Adaptive类
            injectExtension(instance);
            // 这段代码则是本篇的重头戏
            // 循环Wrapper类的缓存(上面取得Class时放入的)
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    // 通过含参的构造方法将SPI实例(根据指定名字创建好的)注入进去
                    // 注入成功并创建好实例之后会把这个组装好的Wrapper实例返回
                    // 这样循环到下一个Wrapper类时其实注入的是上一个Wrapper类实例
                    // 这也解释了为什么后定义的先执行
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

我们举个例子比如说我们在服务提供者文件中定义了如下两个Wrapper类
wrapper1=xxxxxxxxxxxxxxxxxxxxxxxxxx
wrapper2=xxxxxxxxxxxxxxxxxxxxxxxxxx

class Wrapper1 implements Service {
    // 这里进行组装式注入的是真正的Service服务提供者实例
    private Service service;
    public Wrapper1 (Service  service) {
        this.service = service;
    }

    @Override
    public void dosomething() {
        System.out.println("before1");
        service.dosomething();
        System.out.println("after1");
    }
}
class Wrapper2 implements Service {
    // 这里注入的是组装完成的Wrapper1
    private Service service;
    public Wrapper2 (Service  service) {
        this.service = service;
    }

    @Override
    public void dosomething() {
        System.out.println("before2");
        service.dosomething();
        System.out.println("after2");
    }
}

根据上面注入的结果最后执行的打印的顺序是
before2
before1
业务逻辑
after1
after2
通过这样的组装,看好可以实现一层包裹一层,从而实现各个Wrapper以及服务提供者类之间进行解耦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章