为什么要写这篇文章

百度或者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机制示例)_Dubbo SPI 示例(请忽略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 SPIAOPIOC机制

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的参数loadbalancebumblebee,实现了IOC机制。通过调整URLloadbalance的参数值不同,可以动态控制我们要动态注入的实现类是哪个

Dubbo SPI的应用

那么讲了这么多,Dubbo SPI机制到底有什么用呢?我们打开Dubbo官网
Dubbo SPI使用示例(含AOP和IOC机制示例)_SPI_02
可以看到Dubbo提供了大量的扩展点,这些扩展点的实现机制其实都是SPI机制。下面演示一个如何自定义Dubbo负载均衡的例子,来演示Dubbo SPI的应用

Dubbo自定义负载均衡的例子

  1. 实现LoadBalance接口, 以下是DubboLoadBalance接口
@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