dubbo核心---SPI(一)

一、SPI

  1. java中的SPI(service provider interface),数据库驱动 java.sql.Driver, 通过加载在MATA-INF/services下以java.sql.Driver,命名的文件
    在这里插入图片描述
    数据库驱动厂商自己实现,比如mysql
    在这里插入图片描述

  2. dubbo很好的借鉴了这种SPI机制,dubbo会加载META-INF/services,META-INF/dubbo和META-INF/dubbo/internal三个目录下的文件,我们定义一个MyProtocol的协议和org.apache.dubbo.rpc.Protocol的文件
    在这里插入图片描述
    文件内容为:myprotocol=com.gp.patterns.MyProtocol
    在这里插入图片描述
    利用dubbo提供的api测试
    在这里插入图片描述
    执行后,端口为9527,我们看下ExtensionLoader都做了什么。

  3. ExtensionLoader分析

    #1. 获取ExtensionLoader
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    	//EXTENSION_LOADERS缓存对应type的ExtensionLoader
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
    
    #2.获取对应name的实例
    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        //使用默认配置
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        //用于保存name对应的实例
        Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                	#3. 创建这个实例
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
    
    #3. 创建这个实例
    private T createExtension(String name) {
    	#4.获取扩展点class
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
        	//EXTENSION_INSTANCES缓存这个扩展点
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //dubbo的set方法注入实例,用到了dubbo SPI的自适应扩展点,要求set方法的参数必须是一个扩展点
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                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 + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }
    
    #4.获取扩展点class
    private Map<String, Class<?>> getExtensionClasses() {
    	//cachedClasses是一个Holder类
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                	#5.加载扩展点class  并缓存
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
    
    #5.加载扩展点class  并缓存
    private Map<String, Class<?>> loadExtensionClasses() {
    	#6.缓存默认的扩展点   @SPI注解的默认值
        cacheDefaultExtensionName();
    
    	//将三个目录下的文件里面对应的key value 加载进extensionClasses 中
        Map<String, Class<?>> extensionClasses = new HashMap<>();
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
    }
    
    #6.缓存默认的扩展点   @SPI注解的默认值
    private void cacheDefaultExtensionName() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                if (names.length == 1) {
                    cachedDefaultName = names[0];
                }
            }
        }
    }
    

二、dubbo的扩展点

  1. dubbo的静态扩展点

    1.静态扩展点
    public class ExtensionTest {
    
        public static void main(String[] args) {
            //通过名字获取的就是静态扩展点
            Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("myprotocol");
            System.out.println(protocol.getDefaultPort());
        }
        
    }
    
  2. dubbo的自适应扩展点

    1. 自适应扩展点
    public class ExtensionTest {
    
        public static void main(String[] args) {
            //自适应扩展点,返回AdaptiveCompiler,
            Compiler compiler = ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
            compiler.compile("", null);
            System.out.println(compiler);
        }
    
    }
    
    
    1. 根据name自适应用什么compiler, dubbo提供两种 jdk和javassist,默认是@SPI("javassist")2.  @Adaptive在类上表示是一个自适应扩展类
    @Adaptive
    public class AdaptiveCompiler implements Compiler {
    
        private static volatile String DEFAULT_COMPILER;
    
        public static void setDefaultCompiler(String compiler) {
            DEFAULT_COMPILER = compiler;
        }
    
        @Override
        public Class<?> compile(String code, ClassLoader classLoader) {
            Compiler compiler;
            ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
            String name = DEFAULT_COMPILER; // copy reference
            if (name != null && name.length() > 0) {
                compiler = loader.getExtension(name);
            } else {
                compiler = loader.getDefaultExtension();
            }
            return compiler.compile(code, classLoader);
        }
    
    }
    
    
    1.@Adaptive如果在方法上,则动态代理生成对应的扩展点
    2.ExetensionLoader的loadClass方法会判断是不是标注在类上,
    public T getAdaptiveExtension() {
       ....
          instance = createAdaptiveExtension();
       ....
        return (T) instance;
    }
    
    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
    
    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        //loadClass会判断@Adaptive标注在哪里
        //类上
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        //方法上
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
    
    private Class<?> createAdaptiveExtensionClass() {
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }
    
    
  3. dubbo的激活扩展点

    1.类似Springboot的Conditional,
    
    public class ExtensionTest {
    
        public static void main(String[] args) {
            //激活扩展点
            ExtensionLoader<Filter> extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);
            URL url = new URL("", "", 0);
            //url驱动,添加参数就像 dubbo的:@Service(cache = FilterConstants.CACHE_KEY)
            //加了cache,就加载CacheFilter
            url = url.addParameter("cache", "cache");
            List<Filter> filters = extensionLoader.getActivateExtension(url, "cache");
            System.out.println(filters.size());
        }
    
    }
    
    1.group表示能用在服务端还是客户端
    @Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY)
    public class CacheFilter implements Filter {
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值