Java SPI 与 Dubbo SPI
SPI(Service Provider Interface)是JDK内置的一种服务提供发现机制。本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。
在Java中SPI是被用来设计给服务提供商做插件使用的。基于策略模式来实现动态加载的机制。我们在程序只定义一个接口,具体的实现交个不同的服务提供者;在程序启动的时候,读取配置文件,由配置确定要调用哪一个实现。有很多组件的实现,如日志、数据库访问等都是采用这样的方式,最常用的就是 JDBC 驱动。
1. Java SPI
核心类:java.util.ServiceLoader

服务是一组众所周知的接口和(通常是抽象的)类。服务提供者是服务的特定实现。提供者中的类通常实现接口,并子类化服务本身中定义的类。服务提供者可以以扩展的形式安装在Java平台的实现中,即放置在任何常见扩展目录中的jar文件。提供程序也可以通过将它们添加到应用程序的类路径或其他特定于平台的方法来提供。
通过在资源目录META-INF/services中放置一个提供程序配置文件来识别服务提供程序。文件名是服务类型的完全限定二进制名称。该文件包含具体提供程序类的完全限定二进制名的列表,每行一个。每个名称周围的空格和制表符以及空白行将被忽略。注释字符是'#';在每一行中,第一个注释字符之后的所有字符都将被忽略。文件必须用UTF-8编码。
按照上面的方法,我们来写个例子试一下
首先,定义一个接口Car
package org.example;
public interface Car {
void run();
}
两个实现类
ToyotaCar.java
package org.example;
public class ToyotaCar implements Car {
@Override
public void run() {
System.out.println("Toyota");
}
}
HondaCar.java
package org.example;
public class HondaCar implements Car {
@Override
public void run() {
System.out.println("Honda");
}
}
在META-INF/services下创建一个名为org.example.Car的文本文件
org.example.ToyotaCar
org.example.HondaCar
最后,写个测试类运行看一下效果
package org.example; import java.util.ServiceLoader; public class App
{
public static void main( String[] args )
{
ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
serviceLoader.forEach(x->x.run());
}
}

跟一下ServiceLoader的代码,看看是怎么找到服务实现的

用当前线程的类加载器加载

接口和类加载器都有了,万事俱备只欠东风



Java SPI 不足之处:
- 不能按需加载。Java SPI在加载扩展点的时候,会一次性加载所有可用的扩展点,很多是不需要的,会浪费系统资源
- 获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类
- 不支持AOP与IOC
- 如果扩展点加载失败,会导致调用方报错,导致追踪问题很困难
2. Dubbo SPI
Dubbo重新实现了一套功能更强的SPI机制, 支持了AOP与依赖注入,并且利用缓存提高加载实现类的性能,同时支持实现类的灵活获取。
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.8</version>
</dependency>
核心类:org.apache.dubbo.common.extension.ExtensionLoader
先来了解一下@SPI注解,@SPI是用来标记接口是一个可扩展的接口
改造一下前面的例子,在Car接口上加上@SPI注解
package org.example; import org.apache.dubbo.common.extension.SPI; @SPI
public interface Car {
void run();
}
两个实现类不变
在META-INF/dubbo目录下创建名为org.example.Car的文本文件,内容如下(键值对形式):
toyota=org.example.ToyotaCar
honda=org.example.HondaCar
编写测试类
package org.example; import org.apache.dubbo.common.extension.ExtensionLoader; import java.util.ServiceLoader; public class App
{
public static void main( String[] args )
{
// Java SPI
ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
serviceLoader.forEach(x->x.run()); // Dubbo SPI
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car car = extensionLoader.getExtension("honda");
car.run();
}
}
下面跟一下代码


如果缓存Map中有,直接返回,没有则加载完以后放进去






加载策略到底是怎样的呢?



到这里就有点明白了,又看到了熟悉的ServiceLoad.load(),这不是刚才讲的Java SPI嘛




回到之前策略那个地方,将策略按顺序排列,依次遍历所有的策略来加载。就是在那三个目录下查找指定的文件,并读取其中的内容

跟之前的ServiceLoader如出一辙

遇到@Adaptive标注的就缓存起来

下课
Java SPI 与 Dubbo SPI的更多相关文章
- Dubbo 扩展点加载机制:从 Java SPI 到 Dubbo SPI
SPI 全称为 Service Provider Interface,是一种服务发现机制.当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类.所以在程序中并没有直接指定使用接口的哪个实 ...
- dubbo源码分析--dubbo spi解析
1. 什么叫SPI? 简单总结就是一种使用类名字符串来动态实例化java类的方式,也就是反射. 2. java SPI与Dubbo SPI有什么区别 (此图来自网上,我没有刻意去截图) 然后在这个文件 ...
- Dubbo SPI源码解析①
目录 0.Java SPI示例 1.Dubbo SPI示例 2.Dubbo SPI源码分析 SPI英文全称为Service Provider Interface.它的作用就是将接口实现类的全限定名 ...
- Dubbo SPI 和 Java SPI 区别?
JDK SPI JDK 标准的 SPI 会一次性加载所有的扩展实现,如果有的扩展吃实话很耗时,但 也没用上,很浪费资源. 所以只希望加载某个的实现,就不现实了 DUBBO SPI 1,对 Dubbo ...
- 2. Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现(转)
转载自 斩秋的专栏 http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159 SPI接口定义 定义了@SPI注解 public ...
- dubbo SPI设计
SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以在运行时, ...
- 【Dubbo源码阅读系列】之 Dubbo SPI 机制
最近抽空开始了 Dubbo 源码的阅读之旅,希望可以通过写文章的方式记录和分享自己对 Dubbo 的理解.如果在本文出现一些纰漏或者错误之处,也希望大家不吝指出. Dubbo SPI 介绍 Java ...
- 理解 Dubbo SPI 扩展机制
写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dub ...
- 【DUBBO】 Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现
转载:http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577235 SPI接口定义 定义了@SPI注解 public @interfa ...
随机推荐
- JavaDailyReports10_07
动手动脑① 1 package test_1; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 // TO ...
- JAVA基础之this关键之理解
突然觉得有几个知识点需要先复习一下 1.引用和对象并不一定要同时存在,可以只有引用,没有对象 :比如声明String a;如果非得提供一个比喻,可以用电视遥控器和电视来做比喻,遥控器比喻引用,电视 ...
- Turtlebot3入门教程-系统-SBC软件设置(ubuntu20.04)
本文针对如何在树莓派3上安装ubuntu20.04系统和软件进行讲解 树莓派3接上显示屏和鼠标后,开机后继续安装软件包 详细步骤如下: (1)系统安装 (2)ROS安装 (3)TurboBot3依赖的 ...
- linuix查端口
根据进程pid查端口:netstat -nap | grep pid 根据端口port查进程:netstat -nap | grep port 根据pid查找文件的启动位置 ps aux | gre ...
- Apache的Mod_rewrite学习(RewriteRule重写规则的语法) 转
RewriteRuleSyntax: RewriteRule Pattern Substitution [flags] 一条RewriteRule指令,定义一条重写规则,规则间的顺序非常重要.对Apa ...
- Cisco常用命令
• 首次配置网络设备 ○ 需要使用Console线连接进行初始化配置 § 在PC使用"超级终端"或其他软件. • 交换机的工作模式: ...
- 【MyBatis】MyBatis 动态 SQL
MyBatis 动态SQL if 可以根据实体类的不同取值,使用不同的 SQL 语句来进行查询. 使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分. 持久层 DAO 接口: pub ...
- 剑指offer 树的基本操作:四种遍历方式
前序遍历 递归版 编程思想 即借助系统栈,效率较低.二叉树的前序遍历规则:1. 访问根结点: 2. 遍历左子树: 3. 遍历右子树 编程实现 //树的定义 struct TreeNode { int ...
- mmall商城用户模块开发总结
1.需要实现的功能介绍 注册 登录 用户名校验 忘记密码 提交问题答案 重置密码 获取用户信息 更新用户信息 退出登录 目标: 避免横向越权,纵向越权的安全漏洞 MD5明文加密级增加的salt值 Gu ...
- Head First 设计模式 —— 14. 复合 (Compound) 模式
复合模式 在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题. P500 思考题 public interface Quackable { public void quack(); } p ...