1、 Java SPI
Java SPI(Service Provider Interface)是一种在 Java 中实现插件化架构的机制。
SPI 允许开发者定义服务接口,然后实现这些服务接口的不同提供者,使得应用程序在不修改源代码的情况下可以动态地加载、使用不同的服务提供者。
这种机制非常适合于框架和库,因为它允许开发者为特定的功能提供多个实现,而应用程序可以在运行时选择合适的实现。
Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦。
当服务的提供者提供了一种接口的实现之后,需要在**classpath下的META-INF/services/**目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java.util.ServiceLoader。
java SPI实现示例
以下是一个简单的 Java SPI 示例,假设我们有一个服务接口 MyService,以及两个不同的实现提供者 MyServiceImpl1 和 MyServiceImpl2。
1、创建服务接口 MyService
public interface MyService {
void doSomething();
}
2、创建两个实现提供者,MyServiceImpl1 和 MyServiceImpl2
public class MyServiceImpl1 implements MyService {
@Override
public void doSomething() {
System.out.println("MyServiceImpl1 is doing something.");
}
}
public class MyServiceImpl2 implements MyService {
@Override
public void doSomething() {
System.out.println("MyServiceImpl2 is doing something.");
}
}
3、创建 META-INF/services 目录,用于存放服务提供者配置文件。
4、在 META-INF/services 目录下创建一个名为 限定类名MyService 的文件,其中包含服务提供者的类名:
5、创建应用程序代码,使用 SPI 加载并使用服务提供者
package com.lf.java.basic.spi;
import java.util.ServiceLoader;
public class ServiceLoaderTest {
public static void main(String[] args) {
ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
for (MyService service : serviceLoader) {
service.doSomething();
}
}
}
运行结果:
在这个示例中,ServiceLoader 用于加载 MyService 的实现提供者,并调用它们的 doSomething 方法。应用程序不需要知道有哪些具体实现,它们会在运行时动态加载。
确保在编译和运行时正确设置类路径,以便能够找到服务提供者的实现类。这个示例演示了 Java SPI 的基本用法,可以用于更复杂的插件化架构,允许动态地添加、删除或替换服务提供者。
2、Dubbo SPI
Dubbo 的 SPI(Service Provider Interface)机制是其扩展功能的基础,它允许在不修改源码的情况下,通过配置文件或代码注册并加载实现类。
1、Dubbo SPI 的工作原理:
1、扩展接口定义: Dubbo 的各种扩展点(如 Filter、Cluster、LoadBalance)都有对应的接口,定义了可扩展的方法。
2、SPI 配置文件: Dubbo 在 META-INF/dubbo 目录下提供了对应的配置文件,用于声明接口的实现类。比如 META-INF/dubbo/org.apache.dubbo.rpc.Filter 是用于声明 Filter 的实现类。
3、接口实现类: 开发者实现扩展接口的实现类,并在 META-INF/dubbo 下的配置文件中进行注册。
4、SPI 加载机制: Dubbo 在初始化时会根据配置文件加载对应接口的实现类。在使用时,通过接口的工厂方法获取具体的实现类实例。
在 Dubbo 中,Filter、Cluster 和 LoadBalance 都是 Dubbo 中的重要组件,用于处理不同方面的任务。
2、Dubbo SPI的Filter实现示例
Filter 在 Dubbo 中类似于拦截器,用于拦截服务的调用和响应过程。Dubbo 提供了多种 Filter,比如服务调用前后的日志记录、异常处理、权限控制等。
开发者可以实现自定义的 Filter,自定义处理逻辑,并通过配置的方式将其添加到 Dubbo 的调用链中,对服务的调用进行拦截、修改和补充。
1、定义 Filter 接口:
package org.apache.dubbo.rpc;
public interface Filter {
Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
}
2、编写实现类 MyFilter:
package com.example.dubbo;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
@SPI
public class MyFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// 实现自定义 Filter 的逻辑
return invoker.invoke(invocation);
}
}
3、配置文件声明 MyFilter:
在 META-INF/dubbo 目录下创建 org.apache.dubbo.rpc.Filter 文件,并添加:
myfilter=com.example.dubbo.MyFilter
4、使用 MyFilter:
在 Dubbo 注解中使用该 Filter:
@DubboService(filter = "myfilter")
public class HelloServiceImpl implements HelloService {
// 实现服务方法
}
Dubbo 在启动时会自动加载
META-INF/dubbo/org.apache.dubbo.rpc.Filter 文件中声明的 Filter 实现类,使得开发者可以在代码中直接使用自定义的 Filter。
@DubboService(filter = “myfilter”) 注解用于标记 HelloServiceImpl 这个 Dubbo 服务实现类,并通过 filter = “myfilter” 指定了使用名为 myfilter 的 Dubbo Filter。这个过滤器将会在 Dubbo 的服务调用链中生效。
Dubbo Filter 可以被用于多种场景,比如实现对服务的统一日志记录、权限控制、异常处理等。当 Dubbo 服务调用时,注册了指定的 Filter(在这个例子中是 myfilter),Dubbo 框架会在调用前后对服务进行拦截,执行 Filter 中定义的逻辑。
这就是 Dubbo SPI 机制的基本工作原理。
3、Dubbo SPI的Cluster实现示例
Cluster 在 Dubbo 中用于处理服务调用的集群容错。Dubbo 支持多种集群容错策略,比如 Failover、Failfast、Failsafe 等。
开发者可以根据需要自定义 Cluster,实现特定的容错策略,比如自定义重试逻辑、失败处理方式等。Cluster 负责将多个 Invoker(服务提供者)组合成一个,提供给调用者使用。
1、实现自定义 Cluster:
public class MyCluster implements Cluster {
@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
// 自定义 Cluster 的逻辑
return null; // 返回 Invoker 实例
}
}
2、在配置文件中配置该 Cluster:
在 META-INF/dubbo 目录下的 org.apache.dubbo.rpc.Cluster 文件中添加配置:
mycluster=com.example.MyCluster
3、在 Dubbo 注解中使用该自定义 Cluster:
@DubboService(cluster = "mycluster")
public class HelloServiceImpl implements HelloService {
// 实现服务方法
}
4、Dubbo SPI的LoadBalance实现示例
LoadBalance 是 Dubbo 中负责服务调用时的负载均衡策略的组件。Dubbo 提供了多种负载均衡策略,如 RoundRobin、Random、LeastActive 等。
LoadBalance 负责在多个服务提供者中选择合适的节点来进行调用,使得请求能够分布到各个节点,实现负载均衡。开发者可以自定义负载均衡策略,根据实际业务需求选择节点。
1、实现扩展接口: 首先,你需要实现 Dubbo 扩展接口,比如 Filter、Cluster、LoadBalance 等 Dubbo 内置的扩展接口。比如,你可以实现一个自定义的负载均衡策略
public class MyLoadBalance implements LoadBalance {
// 实现 LoadBalance 接口的方法
}
2、配置扩展点: 在 META-INF/dubbo 目录下创建对应的配置文件,比如在 META-INF/dubbo 目录下创建 org.apache.dubbo.rpc.LoadBalance 文件,将自定义的实现类配置到该文件中。
myloadbalance=com.example.MyLoadBalance
3、使用自定义组件: 接下来,在 Dubbo 的 XML 配置文件或者注解配置中,可以使用自定义的组件,比如在 Reference 或 Service 注解中指定使用你自定义的负载均衡策略。
@DubboReference(loadbalance = "myloadbalance")
private HelloService helloService;
3、Java SPI Vs DubboSPI
相似之处
-
服务发现机制: 都是一种服务发现和加载机制,允许通过配置或其他方式注册和加载实现类。
接口与实现分离: 都支持接口与实现分离的方式,使得接口的实现可以独立变更、扩展或替换。
声明式加载: 都支持声明式的方式注册实现类,通过配置文件或注解等方式指定实现类
不同之处
1、实现机制:
Dubbo SPI: Dubbo SPI 是 Dubbo 框架自己实现的一套服务扩展机制,对 Java SPI 进行了一定程度的封装和优化,提供了更灵活、功能更强大的扩展机制。Dubbo SPI 除了通过配置文件声明,还支持通过注解和代码方式进行扩展加载。
Java SPI: Java SPI 是 JDK 自带的一种服务扩展机制,通过在 META-INF/services 目录下提供接口全限定名为文件名的配置文件,声明接口的实现类。
2、灵活性:
Dubbo SPI: Dubbo SPI 提供了更丰富的配置和加载方式,支持注解、配置文件和代码方式进行实现类的注册和加载,具有更高的灵活性。可以指定加载实现类,防止一次加载所有实现类。
Java SPI: Java SPI 只支持配置文件的方式,缺乏灵活性,加载实现类时只能通过 ServiceLoader 进行,而且会加载所有实现类,性能差。
3、扩展性:
Dubbo SPI: Dubbo SPI 为了更好地满足 Dubbo 框架自身的需求,提供了更多的扩展点和机制,比如 Filter、Cluster、LoadBalance 等扩展点,使得扩展更为灵活和方便。
Java SPI: Java SPI 对于扩展点较少,一般用于标准的 JDK 接口扩展,使用场景相对较少。
总的来说,Dubbo SPI 在 Java 原生的 SPI 机制基础上做了进一步的封装和扩展,提供了更多的灵活性和功能性,适用于更复杂的应用场景和更多的扩展点。
而 Java SPI 则是 JDK 自带的一种简单的服务发现机制,用于简单的接口和实现类的加载,功能较为有限。