我是靠谱客的博主 过时毛衣,最近开发中收集的这篇文章主要介绍Dubbo源码---Wrapper详解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"));
}}

最后

以上就是过时毛衣为你收集整理的Dubbo源码---Wrapper详解Dubbo源码—Wrapper详解的全部内容,希望文章能够帮你解决Dubbo源码---Wrapper详解Dubbo源码—Wrapper详解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(59)

评论列表共有 0 条评论

立即
投稿
返回
顶部