概述
1. jdk spi扩展机制
1.1 spi是什么?
spi是service provider interface
,是JDK内置的一种服务提供发现机制。它是针对厂商或插件来进行服务扩展发现的,像JDBC、日志框架等都有用的。简单来说,它是一种动态替换发现的机制。举个简单的例子,如果我们定义了一个规范,需要第三方厂商去实现,那么对于我们应用方来说,只需要集成对应厂商的插件,既可以完成对应规范的实现机制。 形成一种插拔式的扩展手段。
1.2 jdk spi规范
实现SPI需要遵守SPI本身的规范:
-
服务的提供者需要在classpath下创建一个目录,该目录命名必须是:META-INF/services
-
在该目录下创建一个properties文件,该文件需要满足以下几个条件:
-
文件名必须是扩展的接口的全路径名称
-
文件内部描述的是该扩展接口的所有实现类
-
a) 文件的编码格式是UTF-8
-
-
通过java.util.ServiceLoader的加载机制来发现
SPI应用有很多,比如常用的common-logging 日志门面接口,还有java.sql.Driver驱动类,需要第三方厂商去实现接口。
2. dubbo spi 扩展机制
想要弄懂dubbo,理解dubbo的扩展机制是必须的。在dubbo中有许多扩展点,比如有:协议扩展点Portocol、拦截器扩展点Filter、动态代理扩展点ProxyFactory、注册中心扩展点RegistryFactory、负载均衡扩展点LoadBance等等。dubbo为了应对各种场景,它的内部组件都是通过spi扩展机制进行管理的。
2.1 dubbo spi扩展约定
Dubbo默认依次扫描META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/三个classpath目录下的配置文件。配置文件内容为:扩展名=扩展实现类全限定名,以k=v形式。
2.2 扩展点加载类ExtentionLoader
扩展加载器绝对是一个核心组件了 ,它控制着 dubbo 内部所有扩展点的初始化、加载扩展的过程。
1:private static final String SERVICES_DIRECTORY = "META-INF/services/";
2:
3: private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
4:
5: private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
6:
7: private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*");
8:
9: // ============================== 静态属性 ==============================
10:
11: /**
12: * 拓展加载器集合
13: *
14: * key:拓展接口
15: */
16: private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
17: /**
18: * 拓展实现类集合
19: *
20: * key:拓展实现类
21: * value:拓展对象。
22: *
23: * 例如,key 为 Class<AccessLogFilter>
24: * value 为 AccessLogFilter 对象
25: */
26: private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
27:
28: // ============================== 对象属性 ==============================
29:
30: /**
31: * 拓展接口。
32: * 例如,Protocol
33: */
34: private final Class<?> type;
35: /**
36: * 对象工厂
37: *
38: * 用于调用 {@link #injectExtension(Object)} 方法,向拓展对象注入依赖属性。
39: *
40: * 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 属性。
41: */
42: private final ExtensionFactory objectFactory;
43: /**
44: * 缓存的拓展名与拓展类的映射。
45: *
46: * 和 {@link #cachedClasses} 的 KV 对调。
47: *
48: * 通过 {@link #loadExtensionClasses} 加载
49: */
50: private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
51: /**
52: * 缓存的拓展实现类集合。
53: *
54: * 不包含如下两种类型:
55: * 1. 自适应拓展实现类。例如 AdaptiveExtensionFactory
56: * 2. 带唯一参数为拓展接口的构造方法的实现类,或者说拓展 Wrapper 实现类。例如,ProtocolFilterWrapper 。
57: * 拓展 Wrapper 实现类,会添加到 {@link #cachedWrapperClasses} 中
58: *
59: * 通过 {@link #loadExtensionClasses} 加载
60: */
61: private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
62:
63: /**
64: * 拓展名与 @Activate 的映射
65: *
66: * 例如,AccessLogFilter。
67: *
68: * 用于 {@link #getActivateExtension(URL, String)}
69: */
70: private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
71: /**
72: * 缓存的拓展对象集合
73: *
74: * key:拓展名
75: * value:拓展对象
76: *
77: * 例如,Protocol 拓展
78: * key:dubbo value:DubboProtocol
79: * key:injvm value:InjvmProtocol
80: *
81: * 通过 {@link #loadExtensionClasses} 加载
82: */
83: private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
84: /**
85: * 缓存的自适应( Adaptive )拓展对象
86: */
87: private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
88: /**
89: * 缓存的自适应拓展对象的类
90: *
91: * {@link #getAdaptiveExtensionClass()}
92: */
93: private volatile Class<?> cachedAdaptiveClass = null;
94: /**
95: * 缓存的默认拓展名
96: *
97: * 通过 {@link SPI} 注解获得
98: */
99: private String cachedDefaultName;
100: /**
101: * 创建 {@link #cachedAdaptiveInstance} 时发生的异常。
102: *
103: * 发生异常后,不再创建,参见 {@link #createAdaptiveExtension()}
104: */
105: private volatile Throwable createAdaptiveInstanceError;
106:
107: /**
108: * 拓展 Wrapper 实现类集合
109: *
110: * 带唯一参数为拓展接口的构造方法的实现类
111: *
112: * 通过 {@link #loadExtensionClasses} 加载
113: */
114: private Set<Class<?>> cachedWrapperClasses;
115:
116: /**
117: * 拓展名 与 加载对应拓展类发生的异常 的 映射
118: *
119: * key:拓展名
120: * value:异常
121: *
122: * 在 {@link #loadFile(Map, String)} 时,记录
123: */
124: private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
@SPI 、@Adaptive 、@Activate 作用
@SPI (注解在类上) : @SPI 注解标识了接口是一个扩展点 , 属性 value 用来指定默认适配扩展点的名称。
@Activate (注解在类型和方法上) : @Activate 注解在扩展点的实现类上 ,表示了一个扩展类被获取到的的条件,符合条件就被获取,不符合条件就不获取 ,根据 @Activate 中的 group 、 value 属性来过滤 。具体参考 ExtensionLoader 中的 getActivateExtension 函数。
@Adaptive (注解在类型和方法上) : @Adaptive 注解在类上 , 这个类就是缺省的适配扩展。@Adaptive 注解在扩展点 Interface 的方法上时 , dubbo 动态的生成一个这个扩展点的适配扩展类(生成代码 ,动态编译实例化 Class ),名称为 扩展点 Interface 的简单类名 + $Adaptive ,例如 : ProxyFactory$Adpative 。这么做的目的是为了在运行时去适配不同的扩展实例 , 在运行时通过传入的 URL 类型的参数或者内部含有获取 URL 方法的参数 ,从 URL 中获取到要使用的扩展类的名称 ,再去根据名称加载对应的扩展实例 ,用这个扩展实例对象调用相同的方法 。如果运行时没有适配到运行的扩展实例 , 那么就使用 @SPI 注解缺省指定的扩展。通过这种方式就实现了运行时去适配到对应的扩展。
最后
以上就是繁荣帆布鞋为你收集整理的Dubbo源码分析之SPI扩展机制的全部内容,希望文章能够帮你解决Dubbo源码分析之SPI扩展机制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复