为什么要写这篇文章

百度或者Google搜Dubbo SPI机制,出来的基本都是先列出来一个很简单的例子然后开始对源码解析,但是源码解析的时候又会涉及到Dubbo SPI的AOP和IOC机制,所以让人看的云里雾里。本文旨在通过几个简单的例子,带你了解Dubbo SPI的基本使用,本文会例举三个例子,分别讲述Dubbo SPI普通的使用方式,AOP机制和IOC机制。要运行这些例子你需要先引入Dubbo的Jar包。

Dubbo SPI普通的使用方式

新建一个接口​​Robot​​​,并加上​​@SPI​​注解来表示这是一个扩展点

@SPI
public interface Robot {
void sayHello();
}

新建​​Robot​​​的实现类​​OptimusPrime​

public class OptimusPrime implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Optimus Prime.");
}
}

新建​​Robot​​​的实现类​​Bumblebee​

public class Bumblebee implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Bumblebee.");
}
}

在resources/META-INF/dubbo目录下新建文件​​com.bxoon.Robot​​,文件名需要为​​Robot​​的全路径名

Dubbo SPI使用示例(含AOP和IOC机制示例)_ide(请忽略internal和services文件夹)

文件内容如下

optimusPrime = com.bxoon.OptimusPrime
bumblebee = com.bxoon.Bumblebee

新建测试类​​Test​

public class Test {

public static void main(String[] args) {
ExtensionLoader<Robot> extensionLoader =
ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
Robot bumblebee = extensionLoader.getExtension("bumblebee");
bumblebee.sayHello();
optimusPrime.sayHello();
}
}

运行输出内容如下

Hello, I am Bumblebee.
Hello, I am Optimus Prime.

通过本节的代码演示,想必你已经明白了​​Dubbo SPI​​​的基本工作流程,但实际上​​Dubbo SPI​​​的功能远比上诉强大的多,下面我们就一起来了解​​Dubbo SPI​​​的​​AOP​​​和​​IOC​​机制

Dubbo SPI的AOP机制

在上诉工程中新建包装类​​RobotWrapper​

public class RobotWrapper implements Robot {

private Robot robot;

public RobotWrapper(Robot robot) {
this.robot = robot;
}

@Override
public void sayHello() {
System.out.println("包装方法开始");
robot.sayHello();
System.out.println("包装方法结束");
}
}

修改文件​​com.bxoon.Robot​​的内容,新增包装类的全路径名,如下所示

optimusPrime = com.bxoon.OptimusPrime
bumblebee = com.bxoon.Bumblebee
com.bxoon.RobotWrapper

再次运行​​Test​​​类的​​main​​方法,输出如下

包装方法开始
Hello, I am Bumblebee.
包装方法结束
包装方法开始
Hello, I am Optimus Prime.
包装方法结束

可以看到,我们通过新增了一个包装类,即可在扩展点的方法前后实现某些功能,即​​AOP​​机制

Dubbo SPI的IOC机制

要演示​​IOC​​​机制,我们需要在接口方法中新增​​URL​​​参数,并且需要在方法上加上​​@Adaptive​​​注解来指定注入实现类的名称,修改​​Robot​​接口如下

@SPI
public interface Robot {

@Adaptive("loadbalance")
void sayHello(URL url);
}

​Bumblebee​​实现类加上URL参数

public class Bumblebee implements Robot {
@Override
public void sayHello(URL url) {
System.out.println("Hello, I am Bumblebee.");
}
}

​OptimusPrime​​类代码比较重要,修改成如下

public class OptimusPrime implements Robot {
private Robot robot;

public void setRobot(Robot robot) {
this.robot = robot;
}

@Override
public void sayHello(URL url) {
robot.sayHello(url);
System.out.println("Hello, I am Optimus Prime.");
}
}

因为只演示IOS机制,所以删掉RobotWrapper类,同时注释掉​​com.bxoon.Robot​​​文件中配置的​​RobotWrapper​

optimusPrime = com.bxoon.OptimusPrime
bumblebee = com.bxoon.Bumblebee
# com.bxoon.RobotWrapper

修改Test类代码如下

public class Test {

public static void main(String[] args) {
ExtensionLoader<Robot> extensionLoader =
ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
URL url = URL.valueOf("test://localhost/test?loadbalance=bumblebee");
optimusPrime.sayHello(url);
}
}

运行,输出如下

Hello, I am Bumblebee.
Hello, I am Optimus Prime.

可以看到,我们通过指定​​URL​​​的参数​​loadbalance​​​为​​bumblebee​​​,实现了IOC机制。通过调整​​URL​​​中​​loadbalance​​的参数值不同,可以动态控制我们要动态注入的实现类是哪个

Dubbo SPI的应用

那么讲了这么多,Dubbo SPI机制到底有什么用呢?我们打开​​Dubbo​​官网

Dubbo SPI使用示例(含AOP和IOC机制示例)_Dubbo SPI 示例_02

可以看到Dubbo提供了大量的扩展点,这些扩展点的实现机制其实都是SPI机制。下面演示一个如何自定义​​Dubbo​​负载均衡的例子,来演示​​Dubbo SPI​​的应用

Dubbo自定义负载均衡的例子

  1. 实现​​LoadBalance​​​接口, 以下是​​Dubbo​​​的​​LoadBalance​​接口
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
@Adaptive("loadbalance")
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
  1. 自定义一个​​LoadBalance​​接口的实现类
public class DemoLoadBalance implements LoadBalance {
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
System.out.println("[DemoLoadBalance]Select the first invoker...");
return invokers.get(0);
}
}
  1. 添加资源文件 添加文件​​src/main/resource/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance​​,文件内容如下
demo=my=com.demo.dubbo.DemoLoadBalance
  1. 配置使用自定义​​LoadBalance​​​,在​​Consumer​​​端的​​dubbo:reference​​​中配置​​<loadbalance="demo">​
<dubbo:reference id="helloService" interface="com.demo.dubbo.api.IHelloService" loadbalance="demo" />
  1. 如果是​​springboot​​​项目,则在主配置文件​​application.properties​​新增如下
dubbo.provider.loadbalance=gray