zoukankan      html  css  js  c++  java
  • 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);

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

        @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;
        }
    
    }

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

  • 相关阅读:
    Zabbix5 Frame 嵌套
    Zabbix5 对接 SAML 协议 SSO
    CentOS7 安装 Nexus
    CentOS7 安装 SonarQube
    GitLab 后台修改用户密码
    GitLab 查看版本号
    GitLab Admin Area 500 Error
    Linux 安装 PostgreSQL
    Liger ui grid 参数
    vue.js 是一个怪东西
  • 原文地址:https://www.cnblogs.com/z-test/p/9321751.html
Copyright © 2011-2022 走看看