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的方式对其他的扩展点进行注入
  • 相关阅读:
    关于Hibernate(JPA)关联关系加载的默认值
    (转)存储过程语法及实例
    网页qq
    git的代理配置
    The problem with POSIX semaphores 使用信号做进程互斥必看
    git merge conflict的处理
    转载:telnet协议详细描述
    Mac OS X terminal滚动慢的问题
    进程间通讯 信号量函数 semget() semop() semctl()
    .hpp文件和.h文件的区别
  • 原文地址:https://www.cnblogs.com/isdxh/p/14687645.html
Copyright © 2011-2022 走看看