1、Java SPI
1)定义一个接口及其对应的方法;
2)编写该接口的一个实现类;
3)在META-INF/services目录下,创建一个以接口全路径命名的文件;
4)文件内容为具体实现类的全路径名,如果有多个,则用分行符分隔;
5)在代码中通过java.util.ServiceLoader来加载具体的实现类。
2、扩展点加载机制的改进
1)JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现则初始化很耗时,如果没用上也加载,则浪费资源;
2)如果扩展加载失败,则连扩展的名称都获取不到了。Dubbo SPI在扩展加载失败的时候会先抛出真实异常并打印日志;
3)增加了对于扩展IoC和AOP的支持,一个扩展可以直接setter注入其他扩展。
3、扩展点的分类与缓存
- Class缓存:Dubbo SPI获取扩展类时,回先从缓存中读取。如果缓存中不存在,则加载配置文件,根据配置把Class缓存到内存中,并不会直接全部初始化;
- 实例缓存:基于性能考虑,Dubbo框架中不仅缓存Class,也会缓存Class实例化后的对象。每次获取的时候,会先从缓存中读取,如果缓存中读不到,则重新加载并缓存起来。
被缓存的Class和对象实例可以根据不同的特性分为不同的类别:
1)普通扩展类
2)包装扩展类
3)自适应扩展类
4)其他缓存
4、扩展点的特性:
1)自动包装:ExtensionLoader在加载扩展时,如果发现这个扩展类包含其他扩展点作为构造函数的参数,则这个扩展类就会被认为是Wrapper类;
2)自动加载:如果某个扩展类是另外一个扩展点类的成员属性,并且拥有setter方法,那么框架也会自动注入对应的扩展点实例;
3)自适应:在Dubbo SPI中,我们使用@Adaptive注解,可以动态地通过URL中的参数来确定要使用哪个具体的实现类;
4)自动激活:使用@Activate注解,可以标记对应的扩展点默认被激活器用。
5、ExtensionLoader的工作原理:
ExtensionLoader的逻辑入口可以分为getExtension、getAdaptiveExtension、getActivateExtension三个,分别是获取普通扩展类、获取自适应扩展类、获取自动激活的扩展类。
- getActivateExtension方法只是根据不同的条件同时激活多个普通的扩展类;
- getExtension:
1)框架读取SPI对应路径下的配置文件,并根据配置加载所有扩展类并缓存(不初始化);
2)根据传入的名称初始化对应的扩展类;
3)尝试查找符合条件的包装类;
4)返回对应的扩展类实例。
- getAdaptiveExtension:
1)和getExtension一样先加载配置文件;
2)生成自适应类的代码字符串;
3)获取类加载器和编译器,并用编译器编译刚才生成的代码字符串;
4)返回对应的自适应类实例。