提示:学习笔记 欢迎指点
前言
SPI (service provider interface) 机制,SPI是JDK内置的一种服务提供发现机制。目前市面上很多框架都用它来做服务的扩展发现。简单的说,它是一种动态替换发现的机制。
可以把SPI理解为“接口+策略模式+配置文件”实现的动态加载机制。在系统设计中,模块之间通常基于接口编程,不直接显示指定实现类。一旦代码里指定了实现类,就无法在不修改代码的情况下替换为另一种实现。
在了解Dubbo的SPI机制前需要先了解一下Java原生的SPI机制
一、Java SPI机制
从JDK1.6开始引入SPI机制后,可以看到很多使用SPI的案例,比如最常见的数据库驱动实现,在JDK中只定义了java.sql.Driver的接口,具体实现由各数据库厂商来提供。
遵循以下3个标准进行SPI实现
- 在classpath下的META-INF/services/目录里创建一个文件。
- 该文件需要满足以下几个条件 :文件名必须是扩展的接口的全路径名称;文件内部描述的是该扩展接口的所有实现类;文件的编码格式是 UTF-8。
- JDK中查找服务的实现的工具类是:java.util.ServiceLoader。通过 java.util.ServiceLoader 的加载机制来发现
Java SPI 的缺点
- 通过迭代方式,不能按需获取。JDK 标准的 SPI 会一次性加载实例化扩展点的所有实现。就是说 在 META-INF/service 下的文件里面加了 N 个实现类,那么 JDK 启动的时候都会一次性全部加载。如果有的扩展点实现初始化很耗时或者如果有些实现类并没有用到, 就会浪费资源。
- ServiceLoader不是线程安全的。
- 有异常很难定位真正的问题点。如果扩展点加载失败,会导致调用方报错,而且这个错误很难定位到是这个原因。
二、Dubbo中的SPI机制
面向Java SPI 的缺点,Dubbo的SPI机制,是如何进行优化的呢?
Dubbo的SPI实现原理和Java SPI相似,但增强了一些功能和优化。Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。
Dubbo是通过键值对的方式进行配置,我们可以直接通过Key获取我们想要加载的实体类。Dubbo默认的配置文件路径是在./resources/META-INF/dubbo下。
1.几个重要的注解
- @SPI注解,被此注解标记的接口,就表示是一个可扩展的接口,并标注默认值。
- @Adaptive注解,有两种注解方式:一种是注解在类上,一种是注解在方法上。
- @Activate注解,此注解需要注解在类上或者方法上,并注明被激活的条件,以及所有的被激活实现类中的排序信息。
2.Dubbo SPI的特点
- 配置形式更灵活:支持以key:value的形式在文件里配置类似name:xxx.xxx.xxx.xx,后续可以通过name来进行扩展类按需精准获取。
- 缓存的使用:使用缓存提升性能,保证一个扩展实现类至多会加载一次。
- 对扩展类细分扩展:支持扩展点自动包装(Wrapper)、扩展点自动装配、扩展点自适应(@Adaptive)、扩展点自动激活(@Activate)。
扩展点自动包装:wrapper机制,通过装饰者模式可以对其他的扩展点进行增强(wrapper中提供接口类型的构造),同时wrapper中也可以注入其他的扩展点,可以实现类似Spring AOP的功能。
扩展点自动激活: 针对集合类型的扩展点,比如:Filter等,也就是可以多选的场景。
完!