zoukankan      html  css  js  c++  java
  • Dubbo高级进阶Spi应用以及与JDK的Spi区别

    Dubbo官网

    DubboSpi

    Dubbo高级进阶Spi应用

    Dubbo是由阿里巴巴开源的一款高性能、轻量级的开源Java Rpc(远程过程调用)框架,提供三大核心能力:面向接口的远程方法调用、智能容错和负载均衡、服务自动注册与发现。

    在Dubbo的源码中,下面这种句式出现比较多,比如如下句式:通过ExtensionLoader获取Protocol接口的代理类。

    Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();   

    仔细翻看dubbo中的源码,Protocol接口的实现类有很多种,那么在程序的执行中怎么得到对应的实现类,怎么去动态的扩展接口实现,这些问题就是今天讨论的重点。

    一、Spi是什么。

    Spi全称为(Service Provider Interface),是Jdk内置的一种服务提供发现机制,目前有不少框架用他来做服务的拓展发现,简单说Spi是一种动态替换发现机制,使用Spi机制的优势是实现解耦,使第三方服务模块的装配控制逻辑与调用者的业务代码分离。

    二、Java中JDK的Spi实现。

      Java中如果想要使用SPI功能,先提供标准服务接口,然后在提供相关接口实现的调用者,这样就可以通过spi机制中约定好的信息进行查询相应的接口实现

      SPI遵循以下约定

      1)当服务提供者提供了一个服务(接口)的具体实现后,在classpath下的META-INF/services目录下创建一个以“接口全限定名”命名的文件,内容为实现类的全限定名。

      2)接口实现类所在的jar包放在驻车鞥徐的classpath中;

      3)主程序通过java.util.ServiceLoader动态状态实现模块(类),它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM。

      4)Spi的实现类必须携带一个无参构造

      JDK中的Spi机制应用比较广泛比如说common-logging、JDBC等,这里使用JDBC进行举例

    1、JDBC接口定义
      首先在java 中定义接口java.sql.Dirver并没有具体的实现,具体的实现由不同的厂商来提供
    2、mysql实现
      在mysql的jar包mysql-connector-java-6.0.6.jar中,可以找到META-INF/services目录,该目录下会有一个名字为java.sql.Driver的文件,文件内容是com.mysql.cj.jdbc.Driver,这里面的内容就是针对Java中定义的接口的实现。
    3、查找调用在DriverManager类的静态代码块 loadInitialDrivers();方法中

      感兴趣的小伙伴可以去翻看源码哈~这里我就不展示源码了,做一个简单的模拟案例进行展示,项目目录如下所示

    源码如下:

     Driver类

    package city.albert;
    
    /**
     * @author niunafei
     * @function
     * @email niunafei0315@163.com
     * @date 2020/8/25  5:09 PM
     */
    public interface Driver {
    
        /**
         * 模拟获取驱动名称
         * @return
         */
        String driverName();
    }
    View Code

    MysqlDriver实现类

    package city.albert.impl;
    
    import city.albert.Driver;
    
    /**
     * @author niunafei
     * @function
     * @email niunafei0315@163.com
     * @date 2020/8/25  5:12 PM
     */
    public class MysqlDriver implements Driver {
        @Override
        public String driverName() {
            return "mysql 驱动";
        }
    }
    View Code

    OricalDriver实现类

    package city.albert.impl;
    
    import city.albert.Driver;
    /**
     * @author niunafei
     * @function
     * @email niunafei0315@163.com
     * @date 2020/8/25  5:14 PM
     */
    public class OricalDriver implements Driver{
        @Override
        public String driverName() {
            return "orical 驱动";
        }
    }
    View Code

    项目的classpath下META-INF/services目录下创建名为“city.albert.Driver”的文件夹指定你的实现类即可,我这里两个类同时指定内容为:

    city.albert.impl.MysqlDriver
    city.albert.impl.OricalDriver

    测试类如下

    package city.albert.impl;
    
    import city.albert.Driver;
    
    import java.util.ServiceLoader;
    
    /**
     * @author niunafei
     * @function
     * @email niunafei0315@163.com
     * @date 2020/8/25  5:17 PM
     */
    public class TestMain {
    
        public static void main(String[] args) {
            ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
            for (Driver driver : drivers) {
                System.out.println(driver.driverName());
            }
        }
    }

    结果两个实现类都被加载

    三、Dubbo中的Spi实现。

      区别:在Dubbo的框架中并没有使用JDK的Spi来实现,而是重写了Spi的实现,Dubbo中的Spi形式上都是从Jar中加载对应的拓展类,但是Dubbo支持更多的加载路径,也不是通过Iterator的形式调用而是通过名称来定位具体的Provider,按照需要进行加载,并非Jdk中的一次性全部加载,效率更高,同时支持Provider以类似IOC的形式提供。

    Dubbo自己实现Spi的目的
    
    1、JDK标准的SPI会一次性实例化拓展点的所有实现,如果所有的实现初始化很耗时,并没加载上也没有用,就会很浪费资源。
    2、如果有的拓展点加载失败,则所有的拓展点无法使用。
    3、提供了对拓展点包装的功能(Adaptive),并且还支持Set的方式对其他拓展点进行注入

      Dubbo中实现Spi与JDK形式上的区别

      1、接口上需要添加“org.apache.dubbo.common.extension”包下的@SPI注解,在@SPI("spiService")注解中可以指定默认拓展点。

      2、在META-INF/dubbo目录下创建全限定名文件

      3、全限定名文件中的内容是KEY-VALUE形式,value是实现类的全限定类名

      4、调用者使用ExtensionLoader获取加载

    具体实现代码

    api项目中需要引入bubbo依赖因为使用到@Spi注解,版本号根据自己的选型来定哈~

         <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.7.5</version>
         </dependency>

    api代码

    package city.albert;
    
    import org.apache.dubbo.common.extension.SPI;
    
    /**
     * @author niunafei
     * @function
     * @email niunafei0315@163.com
     * @date 2020/8/25  5:49 PM
     */
    @SPI
    public interface DubboSpiService {
        String getName();
    }
    View Code

    实现类

    package city.albert.impl;
    
    import city.albert.DubboSpiService;
    
    /**
     * @author niunafei
     * @function
     * @email niunafei0315@163.com
     * @date 2020/8/25  5:51 PM
     */
    public class DubboSpiServiceImpl implements DubboSpiService{
        @Override
        public String getName() {
            return "dubbo——实现类——getName";
        }
    }
    View Code

    META-INF/dubbo/city.albert.DubboSpiService文件内容

    spiService = city.albert.impl.DubboSpiServiceImpl

    调用类main

    package city.albert;
    
    import org.apache.dubbo.common.extension.ExtensionLoader;
    
    /**
     * @author niunafei
     * @function
     * @email niunafei0315@163.com
     * @date 2020/8/25  5:53 PM
     */
    public class DubboMain {
        public static void main(String[] args) {
            ExtensionLoader<DubboSpiService> loader = ExtensionLoader.getExtensionLoader(DubboSpiService.class);
            DubboSpiService service = loader.getExtension("spiService");
    
            System.out.println(service.getName());
        }
    }

  • 相关阅读:
    EFCore
    PS-邮件发送异常信息
    python-Django
    Autofac
    swagger
    查看哪个程序占用了端口
    SQL SERVER-系统数据库还原
    破解root密码
    WebApi路由
    async,await.task
  • 原文地址:https://www.cnblogs.com/niunafei/p/13553096.html
Copyright © 2011-2022 走看看