为什么要写这篇文章
百度或者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
的全路径名
(请忽略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机制。下面演示一个如何自定义Dubbo
负载均衡的例子,来演示Dubbo SPI
的应用
Dubbo自定义负载均衡的例子
- 实现
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;
}
- 自定义一个
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);
}
}
- 添加资源文件 添加文件
src/main/resource/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance
,文件内容如下
demo=my=com.demo.dubbo.DemoLoadBalance
- 配置使用自定义
LoadBalance
,在Consumer
端的dubbo:reference
中配置<loadbalance="demo">
<dubbo:reference id="helloService" interface="com.demo.dubbo.api.IHelloService" loadbalance="demo" />
- 如果是
springboot
项目,则在主配置文件application.properties
新增如下
dubbo.provider.loadbalance=gray