1、SPI简介

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

2、JDK中的SPI

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

SPI遵循如下约定:

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

入门案例

模块结构如下

(1)api模块

此模块创建了一个接口,供后续模块使用

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

(2)impl模块

此模块中提供了api模块接口的实现类,并且在resource目录下,创建了配置文件。

实现类

package com.lagou.service.impl;
import com.lagou.service.HelloService;
public class HumanHelloService implements HelloService {
    @Override
    public String sayHello() {
        return "hello world";
    }
}

配置文件(名称为接口全限定名, 内容为接口实现类全限定类名)

com.lagou.service.impl.HumanHelloService

(3)main模块

提供了测试jdk-spi的的类

package com.lagou.test;
import com.lagou.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(helloServices.getClass().getName()   ";"   helloService.sayHello());
        }
    }
}

3、Dubbo中的SPI

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

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

4、Dubbo中扩展点使用方式

我们使用三个项目来演示Dubbo中扩展点的使用方式,一个主项目main,一个服务接口项目api,一个服务实现项目impl。

(1)api模块

往pom.xml中导入dubbo依赖

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

创建接口,并在接口上添加上@SPI注解

package com.lagou.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface HelloService {
    String sayHello();
}

(2)impl模块

导入 api项目 的依赖

建立实现类

package com.lagou.service;
public class HumanHelloService implements HelloService{
    @Override
    public String sayHello() {
        return "hello 你好";
    }
}

SPI进行声明操作,在resources 目录下创建目录META-INF/dubbo 目录,在目录下创建名称为接口的全限定类名的文件,文件内部配置实现类名称和对应的全限定名:

human=com.lagou.service.HumanHelloService

(3)main模块

导入坐标、接口项目和实现类项目

创建DubboSpiMain

和原先调用的方式不太相同, dubbo 有对其进行自我重新实现 需要借助ExtensionLoader,创建新的运行项目。这里demo中的示例和java中的功能相同,查询出所有的已知实现,并且调用

package com.lagou;
import com.lagou.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 result = extensionLoader.getExtension(extension).sayHello();
            System.out.println(result);
        }
    }
}

(4)dubbo自己做SPI的目的

1. JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源

2. 如果有扩展点加载失败,则所有扩展点无法使用

3. 提供了对扩展点包装的功能(Adaptive),并且还支持通过set的方式对其他的扩展点进行注入

5、DubboSPI中的Adaptive功能

Dubbo中的Adaptive功能,主要解决的问题是如何动态的选择具体的扩展点。通过getAdaptiveExtension 统一对指定接口对应的所有扩展点进行封装,通过URL的方式对扩展点来进行动态选择。 (dubbo中所有的注册信息都是通过URL的形式进行处理的。)这里同样采用相同的方式进行实现。

(1)创建接口

api中的HelloService 扩展如下方法, 与原先类似,在sayHello中增加Adaptive 注解,并且在参数中提供URL参数.注意这里的URL参数的类为org.apache.dubbo.common.URL,其中@SP可以指定一个字符串参数,用于指明该SPI的默认实现。

package com.lagou.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
@SPI("human") // 这个human对应配置文件中的human
public interface HelloService {
    String sayHello();
    @Adaptive
    String sayHello(URL url);
}

(2)创建实现类

与上面Service实现类代码相似,只需增加URL形参即可

package com.lagou.service;
import org.apache.dubbo.common.URL;
public class HumanHelloService implements HelloService{
    @Override
    public String sayHello() {
        return "hello 你好";
    }
    @Override
    public String sayHello(URL url) {
        return "hello url";
    }
}

(3)编写DubboAdaptiveMain

最后在获取的时候方式有所改变,需要传入URL参数,并且在参数中指定具体的实现类参数

package com.lagou;
import com.lagou.service.HelloService;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class DubboAdaptiveMain {
    public static void main(String[] args) {
        URL url = URL.valueOf("test://localhost?hello.service=human");
        HelloService adaptiveExtension = ExtensionLoader.getExtensionLoader(HelloService.class).getAdaptiveExtension();
        String hello = adaptiveExtension.sayHello(url);
        System.out.println(hello);
    }
}

注意:

  • 因为在这里只是临时测试,所以为了保证URL规范,前面的信息均为测试值即可,关键的点在于hello.service 参数,这个参数的值指定的就是具体的实现方式。关于为什么叫hello.service 是因为这个接口的名称,其中后面的大写部分被dubbo自动转码为. 分割。
  • 通过getAdaptiveExtension 来提供一个统一的类来对所有的扩展点提供支持(底层对所有的扩展点进行封装)。
  • 调用时通过参数中增加URL 对象来实现动态的扩展点使用。
  • 如果URL没有提供该参数,则该方法会使用默认在SPI 注解中声明的实现。

到此这篇关于JDK与Dubbo中的SPI详细介绍的文章就介绍到这了,更多相关JDK与Dubbo的SPI内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

JDK与Dubbo中的SPI详细介绍的更多相关文章

  1. Butterknife 8.1.0在Android Studio 2.1.2中不能与JDK 1.8一起使用

    如果是,我需要做些什么才能使其正常工作?

  2. android-studio – 安卓工作室更新后的问题

    解决方法我在AndroidStudio中花了很多时间来处理这个问题.看来这个问题是由用于编译项目的java版本的差异引起的.最后,在“项目结构”设置窗口中,我在SDK位置选项卡中启用了“使用嵌入式JDK(推荐)”.并快乐编译:)

  3. Android Studio在启动时修改./idea/vcs.xml

    因为不建议忽略AndroidStudio中的整个.idea文件夹,所以大多数文件都由git跟踪.然而奇怪的是,每次启动后,即使已经存在数十个,也会向vcs.xml添加相同的行.这很快变老了.这种行为是有目的还是仅仅是一个错误?AndroidStudio还可以在启动时阻止它进行此类修改吗?

  4. Android-SPI学习笔记

    这篇文章主要介绍了Android-SPI的相关资料,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  5. SpringBoot详细介绍SPI机制示例

    这篇文章主要介绍了深入解析Spring Boot的SPI机制详情,SPI是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要用于框架中开发,更多相关介绍,感兴趣的小伙伴可以参考一下下面文章内容

  6. Dubbo负载均衡策略介绍

    负载均衡改善了跨多个计算资源(例如计算机,计算机集群,网络链接,中央处理单元或磁盘驱动的的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间,并避免任何单个资源的过载

  7. Java中JDK动态代理的超详细讲解

    JDK 的动态代理是基于拦截器和反射来实现的,JDK代理是不需要第三方库支持的,只需要JDK环境就可以进行代理,下面这篇文章主要给大家介绍了关于Java中JDK动态代理的超详细讲解,需要的朋友可以参考下

  8. JDK与Dubbo中的SPI详细介绍

    这篇文章主要介绍了JDK中的SPI与Dubbo中的SPI,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  9. Java Spring Dubbo三种SPI机制的区别

    这篇文章主要介绍了Java Spring Dubbo三种SPI机制的区别,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

  10. 浅析Java中的SPI原理

    SPI:由调用方制定接口标准,实现方来针对接口提供不同的实现,SPI其实就是"为接口查找实现"的一种服务发现机制。本文将浅谈一下SPI机制的原理,需要的可以参考一下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部