在上一篇中讲解了关于Dubbo的SPI机制,在其中有一个很重要的注解@Adaptive,这是一个装饰器,通过此类来获取实现扩展类的具体实现类的方法。因为用到了设计模式中的适配器模式,所以打算独立出来写一篇。
在上一篇中讲解了关于Dubbo的SPI机制,在其中有一个很重要的注解@Adaptive,这是一个装饰器,通过此类来获取实现扩展类的具体实现类的方法。因为用到了设计模式中的适配器模式,所以打算独立出来写一篇。
适配器模式
将一个类的转接口转换成客户希望的另一个接口,适配器模式使得原来由于接口不兼容的而不能的工作的哪些类可以工作,主要作用就是兼容。

类适配器
Adapter类继承了Adaptee(被适配类),同时实现Target接口,在Client类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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(); } }
|
对象适配器
不使用多继承或继承的方式,而是使用直接关联,或者称为委托的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| / 适配器类,直接关联被适配类,同时实现标准接口 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 为例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| 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(); 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
中。

在调用injectExtension向创建的扩展点注入依赖时,有这一句话:
1 2 3 4 5 6 7 8 9
| String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
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
承担着一个适配器的角色。