Dubbo专题

Dubbo之@Adaptive适配器类实现解析

2019-07-26  本文已影响0人  九点半的马拉

在上一篇中讲解了关于Dubbo的SPI机制,在其中有一个很重要的注解@Adaptive,这是一个装饰器,通过此类来获取实现扩展类的具体实现类的方法。因为用到了设计模式中的适配器模式,所以打算独立出来写一篇。

适配器模式

将一个类的转接口转换成客户希望的另一个接口,适配器模式使得原来由于接口不兼容的而不能的工作的哪些类可以工作,主要作用就是兼容。


1.png

类适配器

Adapter类继承了Adaptee(被适配类),同时实现Target接口,在Client类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。

// 已存在的、具有特殊功能、但不符合我们既有的标准接口的类
class Adaptee {
    public void specificRequest() {
        System.out.println("被适配类 具有特殊功能...");
    }
}

// 目标接口,或称为标准接口
interface Target {
    public void request();
}

// 具体目标类,只提供普通功能
class ConcreteTarget implements Target {
    public void request() {
        System.out.println("普通类 具有普通功能...");
    }
}

// 适配器类,继承了被适配类,同时实现标准接口
class Adapter extends Adaptee implements Target{
    public void request() {
        super.specificRequest();
    }
}

// 测试类
public class Client {
    public static void main(String[] args) {
        // 使用普通功能类
        Target concreteTarget = new ConcreteTarget();//实例化一个普通类
        concreteTarget.request();

        // 使用特殊功能类,即适配类
        Target adapter = new Adapter();
        adapter.request();
    }
}

对象适配器

不使用多继承或继承的方式,而是使用直接关联,或者称为委托的方式。

/ 适配器类,直接关联被适配类,同时实现标准接口
class Adapter implements Target{
    // 直接关联被适配类
    private Adaptee adaptee;

    // 可以通过构造函数传入具体需要适配的被适配类对象
    public Adapter (Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    public void request() {
        // 这里是使用委托的方式完成特殊功能
        this.adaptee.specificRequest();
    }
}

// 测试类
public class Client {
    public static void main(String[] args) {
        // 使用普通功能类
        Target concreteTarget = new ConcreteTarget();
        concreteTarget.request();

        // 使用特殊功能类,即适配类,
        // 需要先创建一个被适配类的对象作为参数
        Target adapter = new Adapter(new Adaptee());
        adapter.request();
    }
}

Dubbo中的适配器

Dubbo通过注解@Adaptive作为标记实现了一个适配器类,并且这个类是动态生成的,因此在Dubbo的源码中是看不到代码的,但是我们还是可以看到其实现方式的。Dubbo提供一个动态的适配器类的原因就是可以通过配置文件来动态的使用想要的接口实现类,并且不用改变任何接口的代码,简单来说其也是通过代理来实现的。
在初始化ExtensionLoader时,有一个构造函数,创建对象工厂,获得扩展实现的实例。
获取的就是一个objectFactory 的值是AdaptiveExtensionFactory的实例化对象。

创建 AdaptiveExtensionFactory 实例时,首先获取 ExtensionLoader,此前已经初始化ok。然后依次实例化具体实现(放在ExtensionLoader类cachedClasses属性中,这个属性用于存放实现功能接口的类,即上面解析得到的extensionClasses),即依次实例化SpiExtensionFactory和SpringExtensionFactory,事实上AdaptiveExtensionFactory是通用扩展实现获取的入口,具体的获取方式分为两种,一种是通过Dubbo自己的SPI方式加载到的扩展,另一种是支持复用Spring的方式。以key为“spi”,value是 SpiExtensionFactory 为例。

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        //该类是否实现扩展接口
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error when load extension class(interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + "is not subtype of interface.");
        }
        //判断该类是否为扩展接口的适配器
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            if (cachedAdaptiveClass == null) {
                cachedAdaptiveClass = clazz;
            } else if (!cachedAdaptiveClass.equals(clazz)) {
                throw new IllegalStateException("More than 1 adaptive class found: "
                        + cachedAdaptiveClass.getClass().getName()
                        + ", " + clazz.getClass().getName());
            }
        } else if (isWrapperClass(clazz)) {
            Set<Class<?>> wrappers = cachedWrapperClasses;
            if (wrappers == null) {
                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                wrappers = cachedWrapperClasses;
            }
            wrappers.add(clazz);
        } else {
            //通过反射获得构造器对象
            clazz.getConstructor();
            //未配置扩展名,自动生成,例如DemoFilter为 demo,主要用于兼容java SPI的配置。
            if (name == null || name.length() == 0) {
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }
            // 获得扩展名,可以是数组,有多个拓扩展名。
            String[] names = NAME_SEPARATOR.split(name);
            if (names != null && names.length > 0) {
                Activate activate = clazz.getAnnotation(Activate.class);
                //如果是自动激活的实现类,则加入到缓存
                if (activate != null) {
                    cachedActivates.put(names[0], activate);
                }
                for (String n : names) {
                    if (!cachedNames.containsKey(clazz)) {
                        cachedNames.put(clazz, n);
                    }
                    //缓存扩展实现类
                    Class<?> c = extensionClasses.get(n);
                    if (c == null) {
                        extensionClasses.put(n, clazz);
                    } else if (c != clazz) {
                        throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                    }
                }
            }
        }
    }

从上述代码中可以看出,从配置文件中加载类时,会根据判断注解的不同而存放到不同的变量值中。
如果当前类的注解有@Adaptive,则将它存放到cachedAdaptiveClass中,然后将扩展点的具体实现类存放到extensionClasses中。

2.png

在调用injectExtension向创建的扩展点注入依赖时,有这一句话:

//获得属性,比如StubProxyFactoryWrapper类中有Protocol protocol属性,
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";

//获得属性值,比如Protocol对象,也可能是Bean对象
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
    //注入依赖属性
    method.invoke(instance, object);
}

其中,objectFactory.getExtension(pt, property); 实际上就是AdaptiveExtensionFactory实例去调用cachedClass中的某一个具体类的getExtension方法,因为 AdaptiveExtensionFactory和cachedClass中的类都继承了ExtensionFactory接口,但AdaptiveExtensionFactory没有实现getExtension方法的具体实现,但cacheedClass中的SpringExtensionFactory和spiExtensionFactory都有getExtension方法的具体实现,AdaptiveExtensionFactory承担着一个适配器的角色。

上一篇下一篇

猜你喜欢

热点阅读