Dubbo源码解读与实战 (三) Dubbo SPI 精析,接口实现两极反转(上)

前言

        Dubbo 为了更好地达到 OCP 原则(即“对扩展开放,对修改封闭”的原则),Dubbo 最终决定采用 SPI 机制来加载插件,Dubbo SPI 参考 JDK 原生的 SPI 机制,进行了性能优化以及功能增强。因此,在讲解 Dubbo SPI 之前,我们有必要先来介绍一下 JDK SPI 的工作原理。

JDK SPI

SPI(Service Provider Interface)主要是被框架开发人员使用的一种技术。例如,使用 Java 语言访问数据库时我们会使用到 java.sql.Driver 接口,不同数据库产品底层的协议不同,提供的 java.sql.Driver 实现也不同,在开发 java.sql.Driver 接口时,开发人员并不清楚用户最终会使用哪个数据库,在这种情况下就可以使用 Java SPI 机制在实际运行过程中,为 java.sql.Driver 接口寻找具体的实现。

1. JDK SPI 机制

先做个小例子:先写一个接口:

public interface Robot {
    void sayHello();
}

再写2个实现类:

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

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

在resource/META-INF/services目录下建立一个文件,文件名为 接口的全限定包名+类名,内容为2个实现类

最后写了main函数执行

public static void main(String[] args) throws InterruptedException {
        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
        System.out.println("java spi");
        serviceLoader.forEach(Robot::sayHello);
    }


out:
java spi
Hello, I am Bumblebee.
Hello, I am Optimus Prime.

当服务的提供者提供了一种接口的实现之后,需要在 Classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,此文件记录了该 jar 包提供的服务接口的具体实现类。当某个应用引入了该 jar 包且需要使用该服务时,JDK SPI 机制就可以通过查找这个 jar 包的 META-INF/services/ 中的配置文件来获得具体的实现类名,进行实现类的加载和实例化,最终使用该实现类完成业务功能。

2,Java Spi  在平常的应用。

       我拿我们最常用的JDBC来举例。

        1,首先我们看mysql的jdbc驱动包。resource/META-INF/services包有个java.sql.Driver,里面有个com.mysql.cj.jdbc.Driver.

                这个java.sql.Driver是我们java底层包定义的一个连接驱动接口

        而我们的第三方写的mysql驱动包写了个实现类继承这个实现接口

package com.mysql.cj.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

而在java.sql.DriverManager这个底层类里有个静态块。

static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

跟着loadInitialDrivers这个方法,找到这样一段代码

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

熟悉了把,就是这样,我们的DriverManager类通过java spi 就知道加载哪个链接数据库类。

dubbo也是借鉴这种方式,但是它要更灵活一点,扩展性更好。下一节再介绍。

猜你喜欢

转载自blog.csdn.net/shrek11/article/details/108267672
今日推荐