SPI(Service Provider Interface)--通过接口获取服务

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);
  • 配置

    • 文件位置
//加载顺序 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;
    }

}

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

原文地址:https://www.cnblogs.com/z-test/p/9321751.html

时间: 2024-08-30 10:22:00

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

Java SPI(Service Provider Interface)

SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制. 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现的机制, 举个例子来说,有个接口,想运行时动态的给它添加实现,你只需要添加一个实现. 当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类.当其他的程序需要这个服务的时候,就可以通过查

angularjs factory,service,provider 自定义服务的不同

angularjs框架学了有一段时间了,感觉很好用.可以把angularjs的app理解成php的class,controller是控制器,而内置服务和自定义服务就可以理解成models了.angularjs的内置服务多,例如:$scope,$rootScope,$http,$q,$resource,$routeProvider等等,下面来说一下怎么自定义服务 一,factory,service,provider自定义服务,services.js 'use strict'; /* Service

GPRS GPRS(General Packet Radio Service)是通用分组无线服务技术的简称,它是GSM移动电话用户可用的一种移动数据业务,属于第二代移动通信中的数据传输技术

GPRS 锁定 本词条由“科普中国”百科科学词条编写与应用工作项目 审核 . GPRS(General Packet Radio Service)是通用分组无线服务技术的简称,它是GSM移动电话用户可用的一种移动数据业务,属于第二代移动通信中的数据传输技术.GPRS可说是GSM的延续.GPRS和以往连续在频道传输的方式不同,是以封包(Packet)式来传输,因此使用者所负担的费用是以其传输资料单位计算,并非使用其整个频道,理论上较为便宜.GPRS的传输速率可提升至56甚至114Kbps.[1]

Introduction to the Service Provider Interfaces--官方文档

地址:https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html What Are Services? Services are units of sound-handling functionality that are automatically available when an application program makes use of an implementation of the Java Sound API. T

如何通过Azure Service Management REST API管理Azure服务

通过本文你将了解: 什么是Azure Service Management REST API 如何获取微软Azure 订阅号 如何获取Azure管理证书 如何调用Azure Service Management REST API 什么是Azure Service Management REST API Azure Service Management REST API(之后简称为Azure REST API)是微软开放的一组REST API,它使得Azure用户不仅可以从Azure门户网站上对运

微信接口获取用户信息

必须的参数:appid和appSecret ①获取code 接口链接示例: https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxbafc7fdf3664b600&redirect_uri=http%3a%2f%2f0708.pinzhi365.com%2findex_tb.html&response_type=code&scope=snsapi_userinfo&state=STATE&connec

【转】SoapUI5.0创建WebService接口模拟服务端

原文:http://blog.csdn.net/a19881029/article/details/26348627 使用SoapUI创建WebService接口模拟服务端需要接口描述文件 MathUtil.wsdl: [plain] view plaincopy <?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="http://sean.co

SOA Integration Repository Error:Service Provider Access is not available.

在Oracle EBS Integration Repository中,打开一个Webservice,报了一个警告. 英文: Warning Service Provider Access is not available. You may not be able to see complete information about the service. Please view Service Provider logs for more details 中文: 警告 服务供应商访问权限不可用

Azure Pack和SCVMM集成- 安装Service Provider foundation

1.安装角色和功能 安装如图所示的组件 打开SCVMM安装程序,点击Service Provider Foundation 配置数据库 添加证书 输入服务账户 安装WEB服务