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.csdn.net/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"