zoukankan      html  css  js  c++  java
  • 【Dubbo】04. Dubbo中的SPI

    04. Dubbo中的SPI

    1. SPI简介

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

    2. JDK中的SPI

    截屏2021-04-21 下午10.38.05
    Java中如果想要使用SPI功能,先提供标准服务接口,然后再提供相关接口实现和调用者。这样就可以通过SPI机制中约定好的信息进行查询相应的接口实现。

    2.1 SPI遵循如下约定:

    • 当服务提供者提供了接口的一种具体实现后,在META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
    • 接口实现类所在的jar包放在主程序的classpath中;
    • 主程序通过java.util.ServiceLoader动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;
    • SPI的实现类必须携带一个无参构造方法;

    2.2 示例:

    2.2.1 新建空白maven项目

    创建空白项目——java_spi_demo

    2.2.2 新建module,并定义接口

    创建module——java_spi_demo_api
    新建

    package com.dxh.service;
    
    public interface HelloService {
        String sayHello();
    }
    

    2.2.3 新建服务提供者module,编写实现类

    创建module——java_spi_demo_impl
    pom中加入依赖

    <dependency>
        <groupId>com.dxh</groupId>
        <artifactId>java_spi_demo_api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    

    编写实现类

    package com.dxh.service.impl;
    import com.dxh.service.HelloService;
    
    public class HumanHelloService implements HelloService {
        @Override
        public String sayHello() {
            return "Hello 你好~";
        }
    }
    

    在resource下建立文件夹——META-INF.services
    在META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
    新建文件com.dxh.service.HelloService

    com.dxh.service.impl.HumanHelloService
    

    2.2.4 新建主程序module,编写测试方法

    新建module——java_spi_demo_main
    新建测试方法

    package com.dxh.test;
    
    import com.dxh.service.HelloService;
    
    import java.util.ServiceLoader;
    
    public class JavaSpiMain {
        public static void main(String[] args) {
            final ServiceLoader<HelloService> helloServices = ServiceLoader.load(HelloService.class);
            for (HelloService helloService:helloServices){
                System.out.println(helloService.getClass().getName()+":"+helloService.sayHello());
            }
        }
    }
    

    2.2.5 运行测试

    返回结果:com.dxh.service.impl.HumanHelloService:Hello 你好~

    目录结构:

    3. Dubbo中的SPI

    dubbo中大量的使用了SPI来作为扩展点,通过实现同一接口的前提下,可以进行定制自己的实现类。
    比如比较常见的协议,负载均衡,都可以通过SPI的方式进行定制化,自己扩展。
    Dubbo中已经存在的所有已经实现好的扩展点:

    下图中则是Dubbo中默认提供的负载均衡策略:

    3.1 Dubbo扩展点的使用

    分为三个项目演示:

    • 服务接口项目api
    • 服务实现项目impl
    • 主项目main

    新建maven项目:dubbo_spi_demo

    3.1.1 服务接口项目

    1. 新建module-dubbo_spi_demo_api
    2. pom中增加依赖,用于定义接口时使用@SPI注解
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo</artifactId>
        <version>2.7.5</version>
    </dependency>
    
    1. 新建接口
    package com.dxh.service;
    import org.apache.dubbo.common.extension.SPI;
    
    @SPI
    public interface HelloService {
        String sayHello();
    }
    

    3.1.2 服务端实现项目

    1. 新建module-dubbo_spi_demo_impl
    2. pom中增加依赖
    <dependency>
        <groupId>com.dxh</groupId>
        <artifactId>dubbo_spi_demo_api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    
    1. 新建接口实现类
    package com.dxh.service.impl;
    import com.dxh.service.HelloService;
    
    public class HumanServiceImpl implements HelloService {
        @Override
        public String sayHello() {
            return "Hello 你好";
        }
    }
    
    1. 在resources中新建文件夹META-INF/dubbo,区别于JDK提供的spi的文件名META-INF/services
    2. 在文件夹中新建文件,目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
    human=com.dxh.service.impl.HumanServiceImpl
    

    3.1.3 主程序项目

    1. 新建module-dubbo_spi_demo_main
    2. pom中增加依赖
    <dependency>
        <groupId>com.dxh</groupId>
        <artifactId>dubbo_spi_demo_api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.dxh</groupId>
        <artifactId>dubbo_spi_demo_impl</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    
    1. 新建入口类
      和JDK SPI不同,dubbo对其进行自我重新实现,需要借助ExtensionLoader。
    package com.dxh;
    import com.dxh.service.HelloService;
    import org.apache.dubbo.common.extension.ExtensionLoader;
    import java.util.Set;
    
    public class DubboSpiMain {
        public static void main(String[] args) {
            //获取扩展加载器
            ExtensionLoader<HelloService> extensionLoader = ExtensionLoader.getExtensionLoader(HelloService.class);
            //遍历所有的扩展点 META-INF.dubbo
            Set<String> extensions = extensionLoader.getSupportedExtensions();
            for (String extension : extensions) {
                String sayHello = extensionLoader.getExtension(extension).sayHello();
                System.out.println(sayHello);
            }
        }
    }
    
    1. 运行测试:Hello 你好

    目录结构:

    4. Dubbo自己做SPI的目的

    1. JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源
    2. 如果有扩展点加载失败,则所有扩展点无法使用
    3. 提供了对扩展点包装的功能(Adaptive),并且还支持通过set的方式对其他的扩展点进行注入
  • 相关阅读:
    Chandy-Lamport_algorithm
    3 differences between Savepoints and Checkpoints in Apache Flink
    列数 行数 表数 限制
    数据收集、传输、元数据管理、作业流调度、海量数据查询引擎、数据可视化
    分析云负载均衡产品
    端口被占用通过域名的处理 把www.domain.com均衡到本机不同的端口 反向代理 隐藏端口 Nginx做非80端口转发 搭建nginx反向代理用做内网域名转发 location 规则
    JSON Web Token
    查看开启端口的应用
    If the parts of an organization (e.g., teams, departments, or subdivisions) do not closely reflect the essential parts of the product, or if the relationship between organizations do not reflect the r
    微服务架构的理论基础
  • 原文地址:https://www.cnblogs.com/isdxh/p/14687645.html
Copyright © 2011-2022 走看看