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

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

  • 相关阅读:
    C#针对js escape解码
    .NET XML序列化与反序列化
    数据库文档生成工具(1)
    小欣外贸助手-国际站排名查询工具限量封测了
    七夕小福利
    利用正则表达式获取博客园随笔(四)
    利用正则表达式获取博客园随笔
    利用正则表达式获取博客园随笔(三)
    利用正则表达式获取博客园随笔(二)
    利用正则表达式获取博客园随笔(一)
  • 原文地址:https://www.cnblogs.com/z-test/p/9321751.html
Copyright © 2011-2022 走看看