我是靠谱客的博主 轻松秀发,最近开发中收集的这篇文章主要介绍Dubbo与spi扩展,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

首先了解一下JDK的SPI

  1. spi 全称为(Service Provider Interface),是JDK内置的一种服务提供机制。
  2. 这个是针对厂商或者插件的。
  3. 一般来说对于未知的实现或者对扩展开放的系统,通常会把一些东西抽象出来,抽象的各个模块往往有很多不同的实现方案,例如:日志模块、xml解析模块、jdbc模块等。
实例
package com.tan.spi.example;

public interface People {
	void eat(String food);

}

package com.tan.spi.example;

public class Tom implements People{

	@Override
	public void eat(String food) {
		System.out.print("Tom 在吃"+food);
		
	}

}

package com.tan.spi.example;

import java.util.Iterator;
import java.util.ServiceLoader;

public class Main {
	public static void main(String[] args) {
		System.out.print("llll");
		ServiceLoader<People> load = ServiceLoader.load(People.class);
		Iterator<People> iterator = load.iterator();
		   while (iterator.hasNext()) {
			   People next = iterator.next();
			   next.eat("蛋糕");
	        }
	}
}

src下面必须新建META-INF/services/接口全限定路径
在这里插入图片描述
结果截图
在这里插入图片描述
这里我们只是了解一下,具体还可以自行百度

Dubbo 扩展SPI机制

Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。Dubbo SPI 的相关逻辑被封装在了
ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。

上面我们已经实验了java的spi,现在我们来看一下dubbo自定义实现的spi,这里有一个问题,dubbo为什么不用jdk的spi机制 而要自己定义一个???

  1. jdk标准的spi会一次性实例化扩展点上面的所有实现,如果所有扩展实现会很耗时,没有用到的也进行加载会造成资源浪费
  2. 增加都扩展点ioc和aop的支持,一个扩展点可以直接setter注入其他扩展点

约定

dubbo spi 存储路径 META-INF/dubbo/internal 文件名为接口全路径名,每一个spi定义的格式为扩展名=具体类目

目地

获取一个实现类的对象

途径

  • getExtensionLoader(Class type) 为该接口new 一个ExtensionLoader,然后缓存起来

  • getAdaptiveExtension()
    获取一个扩展装饰类对象,这个类有一个规则,如果没有一个adaptive注解,就会动态创建一个装饰类

  • getExtension(String name)获取一个实现类的对象

具体实现

首先看一下getExtensionLoader的源码,从 org.apache.dubbo.container.Container进去

    private static final ExtensionLoader<Container> loader = 
    ExtensionLoader.getExtensionLoader(Container.class);

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
       // 这里是从一个ConcurrentMap中取,如果没有会将new 一个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;
    }

在看一下getAdaptiveExtension
这是adaptive注解

/**
 * Provide helpful information for {@link ExtensionLoader} to inject dependency extension instance.
 *
 * @see ExtensionLoader
 * @see URL
 */
 // ElementType.TYPE 代表该注解只能注解在类,包上面
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    
    String[] value() default {};

}

需要注意的是,adaptive注解和方法上是存在一定区别的:
注解在类上:代表人工实现编码,即实现了一个装饰类(设计模式中的装饰模式),比如ExtensionFactory
注解在方法上,代表动态的生成和编译一个动态的adaptive,例如Protoco$Adaptive
首先从**org.apache.dubbo.config.spring.schema.DubboNamespaceHandler(dubbo命名空间)**进去,看一下seviceBean.class

    @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

在这里插入图片描述serviceBean实现了ServiceConfig,我们看一下这个方法,
在这里插入图片描述发现里面的getAdaptiveExtension()方法

 public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }

主要看一下createAdaptiveExtension方法,
在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述


    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls;
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        // 拼装路径
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls;
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

最后

以上就是轻松秀发为你收集整理的Dubbo与spi扩展的全部内容,希望文章能够帮你解决Dubbo与spi扩展所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部