1、JavaSPI机制的缺点
Java SPI 在查找扩展实现类的时候遍历 SPI 的配置文件并且将实现类全部实例化,假设一个实现类初始化过程比较消耗资源且耗时,但是你的代码里面又用不上它,这就产生了资源的浪费。
所以说 Java SPI 无法按需加载实现类。
2、DubboSPI
鉴于JavaSPI存在的缺点,Dubbo 就自己实现了一个 SPI。让我们想一下按需加载的话首先你得给个名字,通过名字去文件里面找到对应的实现类全限定名然后加载实例化即可。
Dubbo 就是这样设计的,配置文件里面存放的是键值对。
并且 Dubbo SPI 除了可以按需加载实现类之外,增加了 IOC 和 AOP 的特性,还有个自适应扩展机制。
我们先来看一下 Dubbo 对配置文件目录的约定,不同于 Java SPI ,Dubbo 分为了三类目录。
-
META-INF/services/ 目录:该目录下的 SPI 配置文件是为了用来兼容 Java SPI 。
-
META-INF/dubbo/ 目录:该目录存放用户自定义的 SPI 配置文件。
-
META-INF/dubbo/internal/ 目录:该目录存放 Dubbo 内部使用的 SPI 配置文件。
3、Dubbo简单示例
引入dubbo依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
自定义实现类
public class MyProcol implements Protocol {
@Override
public int getDefaultPort() {
return 8888;
}
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
return null;
}
@Override
public <T> Invoker<T> refer(Class<T> aClass, URL url) throws RpcException {
return null;
}
@Override
public void destroy() {
}
}
在resources目录下新建META-INF/dubbo/internal文件夹,文件夹下新建org.apache.dubbo.rpc.Protocol文件
文件内容为:
myprotocol=com.tianmu.mytest.service.impl.MyProcol
测试
public class DubboDemo {
public static void main(String[] args) {
Protocol protocol= ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("myprotocol");
System.out.println(protocol.getDefaultPort());
}
}
输出
15:43:55.400 [main] INFO org.apache.dubbo.common.logger.LoggerFactory - using logger: org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter
8888
4、面试总结
JDK SPI是一种动态服务提供发现机制,很多框架都用它来做服务的扩展发现。例如JDBC,日志框架
如何使用:
1、在classpath下新建一个目录META-INF/service
2、创建properties文件,文件名必须是扩展接口的全限定名,文件内容是该扩展接口的实现类
3、解析:通过java.util.ServiceLoader的加载机制来发现
JDK SPI的缺点是会一次性加载扩展点的所有实现,如果有的扩展加载耗时或者根本没有用到,就会浪费资源,而且一旦调用出错,很难定位问题
Dubbo SPI没有利用JDK的SPI机制,而是自己实现了一套。在Dubbo中,会存在下面三种代码,分别是自适应扩展点、指定名称扩展点、激活扩展点。对很多组件,都是保留一个接口和多个实现,然后在系统运行的时候动态的根据配置去找到对应的实现类,如果你没有配置,那就走默认的实现类。
如何使用:
1、在resources目录下,创建一个META-INF/dubbo文件夹
META-INF/services/ 目录:该目录下的 SPI 配置文件是为了用来兼容 Java SPI 。
META-INF/dubbo/ 目录:该目录存放用户自定义的 SPI 配置文件。
META-INF/dubbo/internal/ 目录:该目录存放 Dubbo 内部使用的 SPI 配置文件。
2、创建一个文件,名称为扩展接口的全限定名,文件内容和JDK SPI就大不一样了,内容里面配置的是key=value,value就是这个接口的具体哪个实现类,可以指定加载,不用全部加载了
实现原理:
在第2步中配置的接口中,有些方法是被@Adaptive修饰,Dubbo在初始化扩展点时,会生成接口的代理类,里面会实现被@Adaptive修饰的方法,方法里会有一些抽象的通用逻辑,根据解析URL得到的信息,找到并调用真正的实现类。