spi 现在已有实现

  1. jdk 提供实现
  2. dubbo里的spi实现

一、jdk实现

  • 配置
    1. 定义接口
    2. 定义实现类
    3. 配置资源文件 classpath下创建(META-INF/services/接口全面:META-INF/services/spring.design.mode.test4.spi.DogService)

 

  • 调用方法
        ServiceLoader<DogService> loaders = ServiceLoader.load(DogService.class);
for (DogService d : loaders) {
d.sleep();
}
  • 测试结果
黑色dog。。。汪汪叫,不睡觉...
白色dog。。。呼呼大睡觉...
  • 代码下载

    https://files.cnblogs.com/files/z-test/spi-jdk.rar

二、dubbo 里的spi实现。

  • 用法介绍
         //得到一个自适应实现类,用@Adaptive 注解的类,没有就自动生产一个自适应类,可以根据调用方法的参数,动态获取处理类
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); //得到默认的实现类
ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension(); //通过名字得到实现类。得到实现类,先注入实现类,然后,使用它的包装类,进行包装。所以 返回的实例,也可能是包装类的实例
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("name"); //通过url里的 key对应的参数获取实现类。1.先加载默认的激活实现,2加载key对应的value里的值。
ExtensionLoader.getExtensionLoader(Protocol.class).getActivateExtension(url, key);

创建对应名称的扩展类代码

    @SuppressWarnings("unchecked")
private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
  • 配置

    • 文件位置
//加载顺序 DUBBO_INTERNAL_DIRECTORY,DUBBO_DIRECTORY,SERVICES_DIRECTORY
//即用户可以覆盖调源码里的实现。 private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/"; private static final String SERVICES_DIRECTORY = "META-INF/services/";
    • 配置文件内容如下 (文件名:接口全名;内容是key=value:自定义名称:接口实现类全名)

  • 注解解释

    • @SPI("dubbo") 标记接口,提供一个默认的实现 一便于getDefaultExtension()得到默认实现类
    • @Adaptive
      •   1.出现在实现类上,.getAdaptiveExtension() 可以得到对应的实现类,
      •   2.出现在 接口里的对应的方法上  强制要求对应的方法有URL参数,或者参数里包含URL对象。(否则执行报错)
        • 实现类是com.alibaba.dubbo.rpc.Protocol  动态获取协议 根据url.getProtocol() == null ? "dubbo" : url.getProtocol() 获取协议名称,url参数protocol 为空时使用默认名称即@SPI("dubbo")中的名称
        •     public int getDefaultPort() {
          throw new UnsupportedOperationException(
          "method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
          } public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0)
          throws com.alibaba.dubbo.rpc.RpcException {
          if (arg0 == null)
          throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
          if (arg0.getUrl() == null)
          throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
          com.alibaba.dubbo.common.URL url = arg0.getUrl();
          String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
          if (extName == null)
          throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url("
          + url.toString() + ") use keys([protocol])");
          com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader
          .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
          return extension.export(arg0);
          }
        • 接口对应方法中含有com.alibaba.dubbo.rpc.Invocation的根据 url.getMethodParameter(methodName, "cache", "lru");“cache“为参数名,lru 为@spi中指定的默认值
        •     public com.alibaba.dubbo.cache.Cache getCache(com.alibaba.dubbo.common.URL arg0,
          com.alibaba.dubbo.rpc.Invocation arg1) {
          if (arg0 == null)
          throw new IllegalArgumentException("url == null");
          com.alibaba.dubbo.common.URL url = arg0;
          if (arg1 == null)
          throw new IllegalArgumentException("invocation == null");
          String methodName = arg1.getMethodName();
          String extName = url.getMethodParameter(methodName, "cache", "lru");
          if (extName == null)
          throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.cache.CacheFactory) name from url("
          + url.toString() + ") use keys([cache])");
          com.alibaba.dubbo.cache.CacheFactory extension = (com.alibaba.dubbo.cache.CacheFactory) ExtensionLoader
          .getExtensionLoader(com.alibaba.dubbo.cache.CacheFactory.class).getExtension(extName);
          return extension.getCache(arg0, arg1);
          }
        • 其他的实现为url.getParameter("channel.handler", "all") 如果@Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"})  url.getParameter("dispatcher",url.getParameter("dispather", url.getParameter("channel.handler", "all")));
        •     public com.alibaba.dubbo.remoting.ChannelHandler dispatch(com.alibaba.dubbo.remoting.ChannelHandler arg0,
          com.alibaba.dubbo.common.URL arg1) {
          if (arg1 == null)
          throw new IllegalArgumentException("url == null");
          com.alibaba.dubbo.common.URL url = arg1;
          String extName = url.getParameter("dispatcher",
          url.getParameter("dispather", url.getParameter("channel.handler", "all")));
          if (extName == null)
          throw new IllegalStateException(
          "Fail to get extension(com.alibaba.dubbo.remoting.Dispatcher) name from url(" + url.toString()
          + ") use keys([dispatcher, dispather, channel.handler])");
          com.alibaba.dubbo.remoting.Dispatcher extension = (com.alibaba.dubbo.remoting.Dispatcher) ExtensionLoader
          .getExtensionLoader(com.alibaba.dubbo.remoting.Dispatcher.class).getExtension(extName);
          return extension.dispatch(arg0, arg1);
          }
    • @Activate(group = {Constants.PROVIDER, Constants.CONSUMER})  激活的类,适用于getActivateExtension(url, key)  1.先查找适配的@activie对应的类,2,查找url里key的对应的value所对应的类。

  • 动态注入属性(查找set方法,根据set方法参数类型和变量名查找可用对象注入到 spi生产的对象里面。可以注入spi里的对象和springcontext里的对象)

  在 接口类对应的配置文件里,有两个实现类,spi 和spring

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory { private final List<ExtensionFactory> factories; public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
} @Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
} }

spring 获取需要注入的对象

public class SpringExtensionFactory implements ExtensionFactory {

    private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>();

    public static void addApplicationContext(ApplicationContext context) {
contexts.add(context);
} public static void removeApplicationContext(ApplicationContext context) {
contexts.remove(context);
} @Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
for (ApplicationContext context : contexts) {
if (context.containsBean(name)) {
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
return null;
} }

spi

public class SpiExtensionFactory implements ExtensionFactory {

    @Override
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
} }

大家可以看看源码,根据用法介绍里的方法,跟进看看。

SPI(Service Provider Interface)--通过接口获取服务的更多相关文章

  1. Java SPI(Service Provider Interface)

    SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制. 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现的机制, 举个 ...

  2. java的spi(Service Provider Interface)机制及源码(java8)

    1.什么是java的spi spi 全称为 service provider interface 即 服务提供接口,用来作为服务的扩展发现.在运行时动态添加接口的实现,是对接口的实现类的创建管理. 2 ...

  3. JAVA SPI(Service Provider Interface)原理、设计及源码解析(其一)

    背景 团队内部轮流技术分享,其他人都是分享源码,我每次都是设计和架构,感觉自己太特立独行.这次我要合群点,分享点源码. 概念 Service Provider Interface:服务提供方接口.是一 ...

  4. SPI(Service Provider Interface)机制

    JAVA SPI 约定如下:当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/ 目录中同时创建一个以服务接口命名的文件,该文件中的内容就是实现该服务接口的具体 ...

  5. Java SPI(Service Provider Interface)简介

    SPI 简介 SPI 全称为(Service Provider Interface),是JDK内置的一种服务提供发现机制. 一个服务(Service)通常指的是已知的接口或者抽象类,服务提供方就是对这 ...

  6. SPI: Service Provider Interface

    Service Provider Interface: JDK提供的一种服务发现的机制:主要是用于厂商实现JDK的只用. 比如说打印机,JDK提供了一个驱动接口com.printl.printerDr ...

  7. Java中的SPI(Service Provider Interface)

    转自:http://singleant.iteye.com/blog/1497259 最近看到公司的一些框架和之前看到的开源的一些框架的一些服务发现和接入都采用了java的spi机制. 所以简单的总结 ...

  8. Service Provider Interface

    @(Java)[SPI] Service Provider Interface API的一种设计方法,一般用于一些服务提供给第三方实现或者扩展,可以增强框架的扩展或者替换一些组件. 结构 Servic ...

  9. 【Java实战】源码解析Java SPI(Service Provider Interface )机制原理

    一.背景知识 在阅读开源框架源码时,发现许多框架都支持SPI(Service Provider Interface ),前面有篇文章JDBC对Driver的加载时应用了SPI,参考[Hibernate ...

随机推荐

  1. 【Unity|C#】基础篇(17)——字符串处理(String/StringBuilder)

    [学习资料] <C#图解教程>(第25章):https://www.cnblogs.com/moonache/p/7687551.html 电子书下载:https://pan.baidu. ...

  2. JavaDay2(中)

    Java循环与分支练习 习题1: 输出1~100内前5个可以被3整除的数. public class Day2_Test1 { //输出1~100内前5个可以被3整除的数. public static ...

  3. 数据预处理 | 使用 OneHotEncoder 及 get_dummuies 将分类型数据转变成哑变量矩阵

    [分类数据的处理] 问题: 在数据建模过程中,很多算法或算法实现包无法直接处理非数值型的变量,如 KMeans 算法基于距离的相似度计算,而字符串则无法直接计算距离 如: 性别中的男和女 [0,1] ...

  4. caffe+win10+git使用sh文件

    在windows下是否可以执行sh文件呢,搜了一下,可以安装了git就可以执行,当然这不是唯一答案. 然后联想到caffe下有一些.sh文件可以尝试,就用create_mnist.sh尝试把. cre ...

  5. centos yum 安装openresty

    yum 安装openresty sudo yum install yum-utils -y sudo yum-config-manager --add-repo https://openresty.o ...

  6. 2.Ubuntu安装 Docker

    平台支持 Docker CE 支持多种平台,如下表所示 桌面 平台 架构 Docker Desktop for Mac (macOS) X64 Docker Desktop for Windows ( ...

  7. Qtree3 - 树链剖分

    打完以后才发现写复杂了……算了懒得改了 #include <bits/stdc++.h> using namespace std; ],fa[][],size[],wson[],vis[] ...

  8. I+Me=完整的自我

    这是这个系列的第二篇文章,如同第一篇一样,这篇文章会在24小时后删除. 之所以如此极端,因为我自认为这篇文章很有价值,不以这种方式,大家即使看了,也只会一带而过,不会真的汲取到营养. 这篇文章涉及的关 ...

  9. python笔记-02

    Python基础知识 —————————————— A,B,先把A乘以3,然后加上B,最后在加上列表A A = [1, 2, 3, 4, 5, 6] 赋值 B = [1, 2, 3] 变量 定义一个变 ...

  10. JavaScript的Map和Set

    JavaScript的Map和Set 1.map:映射(通过key获得value).增.删 2.set:增.删.判断是否包含某个元素 1. Map <!DOCTYPE html><h ...