概述
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详解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复