SPI的全名为Service Provider Interface.大多数开发人员可能不熟悉。因为这个是针对厂商或者插件的。简单总结下java spi机制的思想。
我们系统里抽象的各个模块,往往有很多不同的实现方案。比如日志模块的方案,xml解析模块,jdbc模块的方案等。面向对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。
一旦代码里设计具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
Java spi就是提供这样的一个机制;为某个接口寻找服务实现的机制。有点类似IOC的思想,就是讲装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。
Jdk提供服务实现查找的一个工具类:java.util.ServiceLoader。
Jdk的SPI的设计目标:
- 面向对象的设计里,模块之间是基于接口编程,模块之间不对实现类进行硬编码。
- 一旦代码里涉及具体的实现类,就违反了可插拔的原则,如果需要替换一种实现,就需要修改代码。
- 为了实现在模块装配的时候,不在模块里面写死代码,这就需要一种服务发现机制。
- Java spi就是提供这样的一种机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是讲装配的控制权转移到代码之外。
Jdk的SPI的具体约定如下:
- 当服务的提供者(provider),提供了一个接口多种实现时,一般会在jar包的META-INF/services/目录下,创建该接口的全类名文件。
- 该文件里面的内容就是该服务接口的具体实现类的全类名名称。
- 当外部加载这个模块的时候,就能通过该jar包META-INF/services/里的配置文件得到具体的实现类名,并加载实例化,完成模块的装配。
Dubbo为什么不采用Jdk的SPI?
- Jdk标准的SPI会一次性实例化扩展点的所有实现,如果扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 增加了对扩展点IOC和AOP的支持,一个扩展点可以直接setter注入其他扩展点。
Dubbo 的SPI有哪些约定?
- Spi文件存储路径 在META-INF/dubbo/internal目录下,并且文件名为接口的全路径名,就是=接口的包名+接口名
- 每个spi文件里面的格式定义为:扩展名=具体的类名,例如:
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtoco
dubbo 的SPI:
目的:获取一个实现类的对象。
途径:ExtensionLoader.getExtension(String name)
实现路径:
- getExtensionLoader(Class
type) 就是为该接口new 一个ExtensionLoader,然后缓存起来。 - getAdaptiveExtension() 获取一个扩展装饰类的对象这个类有一个规则,如果它没有一个@Adaptive注解,就动态创建一个装饰类,例如Protocol$Adaptive对象。
閱讀更多 執念媛 的文章