Dubbo的SPI机制

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_37343985/article/details/88601980

在阅读dubbo的源码的时候,下面这行代码或者相似的代码我们会经常看到

ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) .getExtension(url.getProtocol())

这就是dubbo的SPI机制,这种方式我们平时也经常用,使用场景就是,我们定义了一个接口,有好多类实现了这个接口,程序在编写时我们不知道的用户要使用哪个实现类,由用户在配置文件里面指定,等到需要使用的使用,通过反射来获取具体的实现,提高了程序的扩展性。下面我们就来看看dubbo的SPI机制是如何实现的。
可用于SPI的接口都被添加了SPI注解,如下
在这里插入图片描述
当我们需要一个接口的具体实现的时候首先会调用

ExtensionLoader.getExtensionLoader(XXXX) 
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
		//如果传入了一个null
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        //如果传入的class不是一个接口
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        //如果传入的class没有SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
		//获取传入类型的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;
    }

首先会进行三步检查,在代码里面都注释了,检查通过之后,会尝试从EXTENSION_LOADERS获取当前class的ExtensionLoader,如果没有就新创建一个,再返回。

EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

EXTENSION_LOADERS就是一个map用来保存每个class对应的ExtensionLoader

private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

这是ExtensionLoader私有的构造方法,首先保存了当前class,判断当前class是否是ExtensionFactory.class,如果是则保存null,如果不是调用后面的方法,我们可以发现后面的方法有两个,第一步和我们前面进入的方法是一样的,只不过这一次是ExtensionFactory.class,那么这次objectFactory保存的就是null,我们重点看后面这部分方法
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;
    }

如果不是ExtensionFactory.class,那么objectFactory保存的就是通过createAdaptiveExtension构建的一个对象

private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

在这个方法里面,首先调用getAdaptiveExtensionClass()获得一个class并将它实例化当作参数传入injectExtension里卖弄取包装,我们从里往外看

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

首先调用getExtensionClasses方法获取到实现了用户传入接口的所有实现类并保存起来,如果cachedAdaptiveClass不为空就直接返回,否则dubbo自己拼接一个代理类返回

private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

调用loadExtensionClasses方法返回实现类的名字和对应的类的map集合

private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if (value != null && (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];
            }
        }

        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

在这个方法中最终会调用loadFile方法将指定目录下当前接口对应的文件读入,并获取他们的class对象保存的map中
createAdaptiveExtensionClass方法就是dubbo为添加了SPI注解的接口根据它的方法和方法参数拼接的代理类,从网上copy了一个dubbo生成的代理类如下:

//动态生成的协议代理类
	public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
		public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
		}
		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();
 
			//默认选择dubbo协议,否则根据url中带的协议属性来选择对应的协议处理对象,这样可以动态选择不同的协议
			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])");
 
			//根据拿到的协议key从缓存的map中取协议对象
			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);
		}
		public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
 
			if (arg1 == null) throw new IllegalArgumentException("url == null");
 
			com.alibaba.dubbo.common.URL url = arg1;
 
			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])");
 
			//根据拿到的协议key从缓存的map中取协议对象
			com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
 
			return extension.refer(arg0, arg1);
		}
	}

下面在createAdaptiveExtension方法中就会将这个代理类实例化并传入injectExtension方法中,在里面就是利用反射机制判断接口代理类中是否有需要注入的属性,回退到之前的方法getAdaptiveExtension,将构建的代理类保存到objectFactory中就结束了ExtensionLoader的创建
在dubbo中用户的一切配置都放到了url中,当需要使用接口的某个实现类的时候,首先获取到这个接口对应的ExtensionLoader,然后调用getExtension方法,参数就是url中要使用的实现类的名称

public T getExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        
        //根据传入的name参数确定接口的具体实现类
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        //判断接口实现类是否存在
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    //不存在那么创建一个接口实现类
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
private T createExtension(String name) {

        //根据参数获取接口的Class对象
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
        
            //判断Map中是否存在改Class的实例
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            
                //创建一个实例并保存到map中
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) 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的SPI机制差不多就是这些内容了!!!

展开阅读全文

没有更多推荐了,返回首页