dubbo(二)dubbo spi机制

本文详细介绍了Dubbo的SPI机制,包括其与JDK SPI的区别,以及Dubbo SPI如何加载和缓存实现类。此外,文章还探讨了Adaptive注解的用途,区分了注解在类上和方法上的不同含义,并举例说明了其在框架中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 在介绍dubbo spi机制之前,我们先说一下jdk spi,spi:当服务提供者提供一个接口的多个实现的时候,一般会在META-INF/services/ 下面建立一个与接口全路径同名的文件,在文件里配置接口具体的实现类。当外部调用模块的时候的时候就能实例化对应的实现类而不需要动源码,

2 为什么dubbo不直接用jdk的spi机制,而是自己模仿实现了一个spi机制呢?jdk的spi会在一次实例化所有实现,可能会比较耗时,而且有些可能用不到的实现类也会实例化,浪费资源而且没有选择。另外dubbo的spi增加了对扩展点IOC和AOP的支持,一个扩展点可以直接setter注入其他扩展点。这是jdk spi不支持的。参考博客(https://blog.youkuaiyun.com/xujiajun1994/article/details/81023168

下面详细介绍一下spi实现过程,对应的项目,已经上传github(https://github.com/xuws2gj/dubbo-spi.git

1)项目全览

 

1)代码过程解释

package com.shican.spi.loader;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;

import com.shican.spi.annotation.ShiCanSpi;
import com.shican.spi.util.Holder;

public class ShiCanExtensionLoader<T> {

	/** shicanExtension点的路径 */
	private static final String SHICAN_DIRECTORY = "META-INF/SHICAN/";

	/**
	 * 分割SPI上默认拓展点字符串用的
	 */
	private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");

	/** 扩展点加载器的缓存 */
	private static final ConcurrentHashMap<Class<?>, ShiCanExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ShiCanExtensionLoader<?>>();

	/** 扩展点加缓存 */
	private static final ConcurrentHashMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();

	/**
	 * 接口的class
	 */
	private final Class<?> type;

	/** 保存接口ShiCanSpi注解上的值 */
	private String cachedDefaultName;

	/**
	 * 异常记录
	 */
	private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();

	private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
	private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();

	private static <T> boolean withExtensionAnnotation(Class<T> type) {
		return type.isAnnotationPresent(ShiCanSpi.class);
	}

	private ShiCanExtensionLoader(Class<T> type) {
		this.type = type;
	}

	public static <T> ShiCanExtensionLoader<T> getExtensionLoader(Class<T> type) {
		/***
		 * 判断type 接口参数()
		 */
		if (null == type) {
			//第一次进来type  com.shican.spi.car.Car
			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 @"
					+ ShiCanSpi.class.getSimpleName() + " Annotation!");
		}
        //获取spi的加载类,如果是null则new一个出来再放到缓存中(放置map中)
		// ConcurrentHashMap<Class<?>, ShiCanExtensionLoader<?>> EXTENSION_LOADERS

		ShiCanExtensionLoader<T> loader = (ShiCanExtensionLoader<T>) EXTENSION_LOADERS.get(type);
		if (null == loader) {
			EXTENSION_LOADERS.putIfAbsent(type, new ShiCanExtensionLoader<T>(type));
			loader = (ShiCanExtensionLoader<T>) EXTENSION_LOADERS.get(type);
		}
		return loader;
	}

	@SuppressWarnings("unchecked")
	public T getExtension(String name) {
		if (name == null || name.length() == 0)
			throw new IllegalArgumentException("Extension name == null");
		if ("true".equals(name)) {
			return getDefaultExtension();
		}
		Holder<Object> holder = cachedInstances.get(name);
		if (null == holder) {
			cachedInstances.putIfAbsent(name, new Holder<Object>());
			holder = cachedInstances.get(name);
		}
		Object instance = holder.getT();
		if (null == instance) {
			synchronized (holder) {
				instance = holder.getT();
				if (null == instance) {
					instance = createExtension(name);
					holder.setT(instance);
				}
			}

		}
		return (T) instance;
	}

	/**
	 * 根据获取到的拓展点class实例化成对象返回
	 * 
	 * @param name
	 * @return
	 */
	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(null == instance){
				 EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());//反射生成对象
				 instance = (T)  EXTENSION_INSTANCES.get(clazz);
			 }
			 return instance;
		} catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
		}
	}

	/**
	 * 获取拓展点class,并缓存
	 * 
	 * @return
	 */
	private Map<String, Class<?>> getExtensionClasses() {
		Map<String, Class<?>> classes = cachedClasses.getT();
		if (null == classes) {
			synchronized (cachedClasses) {
				classes = cachedClasses.getT();
				if (null == classes) {
					classes = loadExtensionClasses();
					cachedClasses.setT(classes);
				}
			}
		}
		return classes;
	}

	/**
	 * 1.设置接口默认的实现类名 2.加载文件
	 * 
	 * @return
	 */
	private Map<String, Class<?>> loadExtensionClasses() {
		final ShiCanSpi defaultAnnotation = type.getAnnotation(ShiCanSpi.class);
		if (null != defaultAnnotation) {
			String value = defaultAnnotation.value(); // 拿到注解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, SHICAN_DIRECTORY);
		return extensionClasses;
	}

	/**
	 * 加载本地扩展文件
	 * 
	 * @param extensionClasses
	 * @param shicanDirectory
	 */
	private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
		String fileName = dir + type.getName(); // 固定文件夹 + 接口名全路径

		// 对本地spi扩展文件操作
		try {
			Enumeration<URL> urls;
			ClassLoader classLoader = findClassLoader();
			if (null != classLoader) {
				urls = classLoader.getResources(fileName);
			} else {
				urls = ClassLoader.getSystemResources(fileName);
			}
			if (null != urls) {
				while (urls.hasMoreElements()) {
					java.net.URL url = urls.nextElement();
					try {
						BufferedReader bufferedReader = new BufferedReader(
								new InputStreamReader(url.openStream(), "UTF-8"));
						try {
							String line = null;
							while ((line = bufferedReader.readLine()) != null) {
								final int ci = line.indexOf('#');
								if (ci > 0)
									line = line.substring(0, ci);
								line = line.trim();
								if (line.length() > 0) {
									try {
										String name = null;
										int i = line.indexOf("=");
										if (i > 0) {
											name = line.substring(0, i).trim();
											line = line.substring(i + 1).trim();
										}
										if (line.length() > 0) {
											Class<?> clazz = Class.forName(line, true, classLoader); // 鼓掌!这里终于得到spi(classs)
											if (!type.isAssignableFrom(clazz)) {
												throw new IllegalStateException(
														"Error when load extension class(interface: " + type
																+ ", class line: " + clazz.getName() + "), class "
																+ clazz.getName() + "is not subtype of interface.");
											}
											extensionClasses.put(name, clazz);// 加入缓存
										}
									} catch (Throwable t) {
										IllegalStateException e = new IllegalStateException(
												"Failed to load extension class(interface: " + type + ", class line: "
														+ line + ") in " + url + ", cause: " + t.getMessage(),
												t);
										exceptions.put(line, e);
									}
								}
							}
						} finally {
							bufferedReader.close();
						}
					} catch (Exception e) {
						// ignore...
					}
				}
			}
		} catch (Exception e) {
			// ignore...
		}

	}

	private static ClassLoader findClassLoader() {
		return ShiCanExtensionLoader.class.getClassLoader();
	}

	public T getDefaultExtension() {
		getExtensionClasses();
		if (null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) {
			return null;
		}
		return getExtension(cachedDefaultName);
	}

	// 异常提示
	private IllegalStateException findException(String name) {
		for (Map.Entry<String, IllegalStateException> entry : exceptions.entrySet()) {
			if (entry.getKey().toLowerCase().contains(name.toLowerCase())) {
				return entry.getValue();
			}
		}
		StringBuilder buf = new StringBuilder("No such extension " + type.getName() + " by name " + name);

		int i = 1;
		for (Map.Entry<String, IllegalStateException> entry : exceptions.entrySet()) {
			if (i == 1) {
				buf.append(", possible causes: ");
			}

			buf.append("\r\n(");
			buf.append(i++);
			buf.append(") ");
			buf.append(entry.getKey());
			buf.append(":\r\n");
			buf.append(entry.getValue().toString());
		}
		return new IllegalStateException(buf.toString());
	}
}
package com.shican.spi.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 
 * @author shican
 *
 */

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ShiCanSpi {
	
	String value() default "";

}
package com.shican.spi.car;

import com.shican.spi.annotation.ShiCanSpi;

@ShiCanSpi("smallCar")
public interface Car {
	
	void drive();

}
#shican -spi
bigCar=com.shican.spi.car.impl.BigCar
smallCar=com.shican.spi.car.impl.SmallCar
package com.shican.spi.util;

public class Holder<T> {
	
	private volatile T t;

	public T getT() {
		return t;
	}

	public void setT(T t) {
		this.t = t;
	}

}

关于SchiCanExtensionLoader类,运行test

1 先进入getExtensionLoader方法

public static <T> ShiCanExtensionLoader<T> getExtensionLoader(Class<T> type) {
		/***
		 * 判断type 接口参数()
		 */
		if (null == type) {
			//第一次进来type  com.shican.spi.car.Car
			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 @"
					+ ShiCanSpi.class.getSimpleName() + " Annotation!");
		}
        //获取spi的加载类,如果是null则new一个出来再放到缓存中(放置map中)
		// ConcurrentHashMap<Class<?>, ShiCanExtensionLoader<?>> EXTENSION_LOADERS

		ShiCanExtensionLoader<T> loader = (ShiCanExtensionLoader<T>) EXTENSION_LOADERS.get(type);
		if (null == loader) {
			EXTENSION_LOADERS.putIfAbsent(type, new ShiCanExtensionLoader<T>(type));
			loader = (ShiCanExtensionLoader<T>) EXTENSION_LOADERS.get(type);
		}
		return loader;
	}

获取一个spi加载器实例,第一次进来为null则先new一个出来,再放到缓存之中(ConcurrentHashMap<Class<?>, ShiCanExtensionLoader<?>> EXTENSION_LOADERS)EXTENSION_LOADERS,type此时为接口路径名

2 进入getExtension方法

public T getExtension(String name) {
		if (name == null || name.length() == 0)
			throw new IllegalArgumentException("Extension name == null");
		if ("true".equals(name)) {
			return getDefaultExtension();
		}
		Holder<Object> holder = cachedInstances.get(name);
		if (null == holder) {
			cachedInstances.putIfAbsent(name, new Holder<Object>());
			holder = cachedInstances.get(name);
		}
		Object instance = holder.getT();
		if (null == instance) {
			synchronized (holder) {
				instance = holder.getT();
				if (null == instance) {
					instance = createExtension(name);
					holder.setT(instance);
				}
			}

		}
		return (T) instance;
	}
此时根据getExtension(name)name名,相等于key,第一步先判断是否是默认实现是的话走getDefaultExtension,否则接下来,此时ConcurrentMap<String, Holder<Object>> cachedInstances作用存放对应实现instance,及key/value,先从cachedInstances缓存中取,没有则先new一个Holder,Holder结构即保存了一个Class的对象,getT()可以取出对象,而cachedInstances缓存Holder,就等于缓存这个实现类对象
2 进入createExtension方法
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(null == instance){
				 EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());//反射生成对象
				 instance = (T)  EXTENSION_INSTANCES.get(clazz);
			 }
			 return instance;
		} catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
		}
	}

  3 进入getExtensionClasses方法

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

 

先从cachedClasses(Holder中)缓存中取这个class,没有进入

4 进入loadExtensionClasses方法

private Map<String, Class<?>> loadExtensionClasses() {
		final ShiCanSpi defaultAnnotation = type.getAnnotation(ShiCanSpi.class);
		if (null != defaultAnnotation) {
			String value = defaultAnnotation.value(); // 拿到注解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, SHICAN_DIRECTORY);
		return extensionClasses;
	}
4 进入loadFile方法
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
		String fileName = dir + type.getName(); // 固定文件夹 + 接口名全路径

		// 对本地spi扩展文件操作
		try {
			Enumeration<URL> urls;
			ClassLoader classLoader = findClassLoader();
			if (null != classLoader) {
				urls = classLoader.getResources(fileName);
			} else {
				urls = ClassLoader.getSystemResources(fileName);
			}
			if (null != urls) {
				while (urls.hasMoreElements()) {
					java.net.URL url = urls.nextElement();
					try {
						BufferedReader bufferedReader = new BufferedReader(
								new InputStreamReader(url.openStream(), "UTF-8"));
						try {
							String line = null;
							while ((line = bufferedReader.readLine()) != null) {
								final int ci = line.indexOf('#');
								if (ci > 0)
									line = line.substring(0, ci);
								line = line.trim();
								if (line.length() > 0) {
									try {
										String name = null;
										int i = line.indexOf("=");
										if (i > 0) {
											name = line.substring(0, i).trim();
											line = line.substring(i + 1).trim();
										}
										if (line.length() > 0) {
											Class<?> clazz = Class.forName(line, true, classLoader); // 鼓掌!这里终于得到spi(classs)
											if (!type.isAssignableFrom(clazz)) {
												throw new IllegalStateException(
														"Error when load extension class(interface: " + type
																+ ", class line: " + clazz.getName() + "), class "
																+ clazz.getName() + "is not subtype of interface.");
											}
											extensionClasses.put(name, clazz);// 加入缓存
										}
									} catch (Throwable t) {
										IllegalStateException e = new IllegalStateException(
												"Failed to load extension class(interface: " + type + ", class line: "
														+ line + ") in " + url + ", cause: " + t.getMessage(),
												t);
										exceptions.put(line, e);
									}
								}
							}
						} finally {
							bufferedReader.close();
						}
					} catch (Exception e) {
						// ignore...
					}
				}
			}
		} catch (Exception e) {
			// ignore...
		}

	}

这个方法,通过流读取文件方式,把META-INF/SHICAN文件中的实现类都放在extensionClasses(Map<String, Class<?>)这个map之中

然后再返回到getExtensionClasses方法中把这个map放在了cachedClasses之中(即holder)

再返回到createExtension方法 根据name值取到对应的实现类,这时候在查看EXTENSION_INSTANCES是否有这个实现类,没有就把放进去即放入EXTENSION_INSTANCES缓存

再返回到getExtension方法,把拿出来的实现类放到cachedInstances之中,cachedClasses缓存所有实现类,cachedInstances缓存已经根据key即name取到的具体的实现类

再返回到test方法,用具体的实现类对象执行方法

 

(二) 关于dubbo -Adaptive详解

为什么要设计adaptive?注解在类上和注解在方法上的区别?
adaptive设计的目的是为了识别固定已知类和扩展未知类。
1.注解在类上:代表人工实现,实现一个装饰类(设计模式中的装饰模式),它主要作用于固定已知类,
目前整个系统只有2个,AdaptiveCompiler、AdaptiveExtensionFactory。(所以方法上有adaptive注解走AdaptiveCompiler.compile()再选择abstractcompile两个子类 jdkcompile还是javassistcompile(默认选择因为compile接口上有spi注解(javassist)))
a.为什么AdaptiveCompiler这个类是固定已知的?因为整个框架仅支持Javassist和JdkCompiler。
a.为什么AdaptiveExtensionFactory这个类是固定已知的?因为整个框架仅支持2个objFactory,一个是spi,另一个是spring
2.注解在方法上:代表自动生成和编译一个动态的Adpative类,它主要是用于SPI,因为spi的类是不固定、未知的扩展类,所以设计了动态$Adaptive类.
例如 Protocol的spi类有 injvm dubbo registry filter listener等等 很多扩展未知类,
它设计了Protocol$Adaptive的类,通过ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi类);来提取对象

关于这个注解,我选了比较好的博客介绍一下

https://www.liangzl.com/get-article-detail-5461.html

https://segmentfault.com/a/1190000013342968?utm_source=tag-newest

 

5 总结
dubbo spi机制,即定义了各种实现类的配置文件,再根据具体的key寻找具体实现类的过程,中间经历各种放入缓存操作,读取文件及反射,达到不同实现的扩展。而Adaptive注解注在类上,代表已知实现类,优先使用,注在接口的方法上,代表会生成一个$Adaptive适配类, 下一节,我们谈论一下dubbo暴露服务的原理

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值