Dubbo源码---Wrapper详解

Dubbo源码—Wrapper详解

Wrapper

wrapper为Dubbo SPI扩展类的包装对象,其实际作用起到了代理的作用。当Dubbo在解析SPI配置时,如果有相关的包装类,将被缓存在cachedWrapperClasses中,在执行getExtension时,如果需要包装类,将返回该扩展类的包装类。

Wrapper注解

Dubbo提供了Wrapper注解,当包装类上无此注解作用时,表示该扩展的所有扩展类都将会被该Wrapper类代理;否则需要在该注解上根据该扩展的扩展类名称,通过matches指定需要代理的扩展类,或通过mismatches指定不需要代理的扩展类。

源码分析

Wrapper包装类解析

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                       boolean overridden) throws NoSuchMethodException {
    // 忽略这里的无关代码
  
    // 检测目标类上是否有@Adaptive注解
    if (clazz.isAnnotationPresent(Adaptive.class)) {
     // 忽略这里的无关代码
     // 检测类是否是wrapper类型
    } else if (isWrapperClass(clazz)) {
        // 设置缓存
        cacheWrapperClass(clazz);
    // 到了这里表示这是一个普通的扩展类
    } else {
        //忽略这里的无关代码
    }
}

loadClass方法是在Dubbo SPI配置文件解析时被调用的一个方法,其入口为ExtensionLoader中的getExtensionClasses方法,从上面可以看出,在解析阶段,如果在SPI配置文件中存在满足Wrapper包装类约定的扩展类,将被添加到缓存,下面来看下Wrapper包装类时如何进行约定以及添加到缓存的。

private boolean isWrapperClass(Class<?> clazz) {
    try {
        clazz.getConstructor(type);
        return true;
    } catch (NoSuchMethodException e) {
        return false;
    }
}private void cacheWrapperClass(Class<?> clazz) {
    if (cachedWrapperClasses == null) {
      cachedWrapperClasses = new ConcurrentHashSet<>();
    }
    cachedWrapperClasses.add(clazz);
 }

从isWrapperClass方法可知,判断为包装类的约定是该类必须有一个构造函数,且参数为该扩展类。从其缓存方法cacheWrapperClass可以看出,一个扩展类是可以存在多个Wrapper类的。

Wrapper包装类获取

private T createExtension(String name, boolean wrap) {
    // 忽略无关代码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);// 使用包装类,如果存在该类的包装类,则不返回该类的实例,返回该类的包装类
        if (wrap) {List<Class<?>> wrapperClassesList = new ArrayList<>();
            // 获取该类的所有包装类
            if (cachedWrapperClasses != null) {
                wrapperClassesList.addAll(cachedWrapperClasses);
                // 对不同的包装类进行排序
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                // 这个方法看似简单,就是简单的遍历,实际上在存在多个包装类的情况下,会将上一次循环获取到的包装类通过构造方法注入到新的包装类
                // 这样就保证了在存在多个包装类的情况下,多个包装类都可以可行,值得一提的是,可以对不同的包装类指定执行顺序
                // 可通过Activate注解进行排序
                for (Class<?> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    // 如果扩展类使用类@Wrapper标注,表示该类不要使用包装类
                    if (wrapper == null
                            || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                        //
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                    }
                }
            }
        }
        // 初始化扩展类,主要是判断该扩展类是否实现类Lifecycle,如果实现了则执行该类的initialize()方法
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}

createExtension方法是创建Extension实例的方法,其发生在Dubbo SPI配置文件被解析后,其入口为ExtensionLoader中的getExtension方法。从上面的源码中可以看到,当需要Wrapper类时,将获取到Dubbo SPI解析阶段缓存的Wrapper类,进行排序后开始遍历,使用反射实例化Wrapper后进行IOC注入,注意这一句:instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)),每次循环都会将上一次获取到的Wrapper实例作为构造方法的参数注入到新的Wrapper实例中,以此实现多层扩展类多Wrapper的代理。

多Wrapper优先级

在Wrapper类上添加@Activate注解,通过设置order进行排序,越小优先级越高

示例

@SPI("ali")
public interface Pay {
    void pay(URL url);
}

扩展类

public class AliPay implements Pay{
    @Override
    public void pay(URL url) {
        System.out.println("支付宝支付");
    }
}

Wrapper类

@Activate(order = 2)
public class PayWrapper1 implements Pay{
    private Pay pay;
    public PayWrapper1(Pay pay) {
        this.pay = pay;
    }
    @Override
    public void pay(URL url) {
        System.out.println("pay before...");
        pay.pay(url);
        System.out.println("pay after...");
    }
}@Activate(order = 1)
public class PayWrapper2 implements Pay{private Pay pay;public PayWrapper2(Pay pay) {
        this.pay = pay;
    }@Override
    public void pay(URL url) {
        System.out.println("wrapper2 pay before...");
        pay.pay(url);
        System.out.println("wrapper2 pay after...");
    }
}

Dubbo SPI配置

# 该配置的路径为:META-INF/dubbo/,文件名为Pay类的全限定名
ali=org.apache.dubbo.demo.provider.javaspi.AliPay
wrapper=org.apache.dubbo.demo.provider.javaspi.PayWrapper1

测试类

public class JavaSpiTest {/**
     * 不使用包装类
     * 打印信息为:支付宝支付
     */
    @Test
    public void test1() {
        ExtensionLoader<Pay> extensionLoader =
                ExtensionLoader.getExtensionLoader(Pay.class);
        Pay ali1 = extensionLoader.getExtension("ali", false);
        ali.pay(URL.valueOf("http://localhost:9999/xxx"));
    }
  
  /**
     * 使用包装类
     * 打印信息为:
     * wrapper2 pay before...
     * pay before...
     * 支付宝支付
     * pay after...
     * wrapper2 pay after...
     */
    @Test
    public void test2() {
        ExtensionLoader<Pay> extensionLoader =
                ExtensionLoader.getExtensionLoader(Pay.class);
        Pay ali1 = extensionLoader.getExtension("ali", true);
        ali.pay(URL.valueOf("http://localhost:9999/xxx"));
    }}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值