Dubbo SPI-Wrapper

前言

在Dubbo SPI中是通过Wrapper实现AOP,对于AOP相信大家都不陌生,这里不做的过多的介绍,我们主要来了解Dubbo SPI中是如何使用Wrapper类以及实现的细节。

使用场景

Dubbo 中的一个扩展接口可以有多个扩展实现类,这些扩展实现类可能会包含一些相同的逻辑,如果在每个实现类中都写一遍,那么这些重复代码就会变得很难维护。因此Dubbo提供的自动包装特性(Wrapper),来解决这个问题。Dubbo将多个扩展实现类的公共逻辑,抽象到Wrapper类中,同时Wrapper也实现了扩展接口,在获取真正的扩展实现对象时,相当于在其外面包装一层Wrapper对象,可以理解成一层装饰器,通过这样就可以在方法执行前后调用公共方法,也是一种AOP的体现。

举个栗子

  1. 定义接口;
@SPI
public interface WrapperDemo {
    void test();
}
  1. 定义接口实现;
public class WrapperDemoImpl implements WrapperDemo{
    @Override
    public void test() {
        System.out.println("WrapperDemoImpl test 方法执行");
    }
}
  1. 创建计算耗时包装类以及Order包装类;
public class CalWasteTimeWrapper implements WrapperDemo {

    private WrapperDemo wrapperDemo;

    public CalWasteTimeWrapper(WrapperDemo wrapperDemo) {
        this.wrapperDemo = wrapperDemo;
    }

    @Override
    public void test() {
        System.out.println("执行cal wrapper");
        System.out.println("执行统计耗时开始");
        long startTime = System.currentTimeMillis();
        wrapperDemo.test();
        System.out.println("执行耗时" + (System.currentTimeMillis() - startTime));
    }
}

public class OrderWrapper implements WrapperDemo {

    private WrapperDemo wrapperDemo;

    public OrderWrapper(WrapperDemo wrapperDemo) {
        this.wrapperDemo = wrapperDemo;
    }

    @Override
    public void test() {
        System.out.println("执行order wrapper");
        wrapperDemo.test();
    }
}
  1. 定义配置文件;
wrapperDemo=org.dubbo.spi.example.wrapper.WrapperDemoImpl
calWasteTimeWrapper=org.dubbo.spi.example.wrapper.CalWasteTimeWrapper
orderWrapper=org.dubbo.spi.example.wrapper.OrderWrapper
  1. 测试,这里我们会发现wrapper的顺序,越往下的越先被调用;
public class Test {
    public static void main(String[] args) {
        WrapperDemo wrapperDemo = ExtensionLoader
                .getExtensionLoader(WrapperDemo.class)
                .getExtension("wrapperDemo")
;
        wrapperDemo.test();
    }
}
Dubbo SPI-Wrapper
image.png

源码

关于源码部分我们开始介绍时候已经带出来过关于包装类相关的介绍,这里我们主要挑重点代码进行介绍,核心可以分为两步,一步是加载时候,另外执行代码创建时候;

加载

加载就是在loadClass方法时候扩展类加载,判断是否是包装类进行加载,将所有的包装类的信息加载到ConcurrentHashSet中,对于包装类的判断也很简单,就是判断该类里面是否有type类型的构造参数,如果有就是包装类,否则会抛出异常, 返回false;

    //包装类判断
    else if (isWrapperClass(clazz)) {
       //缓存包装类
       cacheWrapperClass(clazz);
    }

    //使用ConcurrentHashSet缓存
    private void cacheWrapperClass(Class<?> clazz) {
        if (cachedWrapperClasses == null) {
            cachedWrapperClasses = new ConcurrentHashSet<>();
        }
        cachedWrapperClasses.add(clazz);
    }

    //判断是否是包装类
    private boolean isWrapperClass(Class<?> clazz) {
        try {
            clazz.getConstructor(type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

创建

创建部分是在createExtension方法中,对包装类进行创建,对于instance最后生成是WrapperDemo类型对象,里面包含OrderWrapper和CalWasteTimeWrapper对象,所以在最后调用的时候都进行输出,于是乎就有了Aop的效果;Dubbo SPI-Wrapper

    @SuppressWarnings("unchecked")
    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) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                //采用反射创建对应实例
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //向对象中注入依赖的属性 IOC自动装配
            injectExtension(instance);


            if (wrap) {
                //创建Wrapper扩展对象
                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作为参数创建wrapper实例,然后向wrapper实例中注入属性值
                            //并将wrapper实例赋值给instance
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }
            //环境初始化
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

结束

欢迎大家点点关注,点点赞!


原文始发于微信公众号(既然遇见不如同行):Dubbo SPI-Wrapper

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/52284.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!