共计 6418 个字符,预计需要花费 17 分钟才能阅读完成。
1、Dubbo 的 SPI 例子
@SPI | |
public interface Robot {void sayHello(); | |
} | |
public class OptimusPrime implements Robot{ | |
@Override | |
public void sayHello() {System.out.println("Hello, I am Optimus Prime."); | |
} | |
} | |
public class Bumblebee implements Robot{ | |
@Override | |
public void sayHello() {System.out.println("Hello, I am Bumblebee."); | |
} | |
} |
public class DubboSPITest { | |
@Test | |
public void sayHelloDubbo() throws Exception {ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); | |
Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); | |
optimusPrime.sayHello(); | |
Robot bumblebee = extensionLoader.getExtension("bumblebee"); | |
bumblebee.sayHello();} | |
} |
输出: | |
Hello, I am Optimus Prime. | |
Hello, I am Bumblebee. |
2、Dubbo 的 SPI 源码分析
ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.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!"); | |
} | |
// 从缓存中获取与拓展类对应的 ExtensionLoader | |
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); | |
if (loader == null) {// 若缓存未命中, 则创建一个新的实例, 先简单看下 new ExtensionLoader<T>(type) | |
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); | |
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); | |
} | |
return loader; | |
} |
private ExtensionLoader(Class<?> type) { | |
this.type = type; | |
// 这里的 type 是 Robot.class, 所以会执行后面的代码, 加载并创建 SpiExtensionFactory 和 SpringExtensionFactory, | |
// 后面再分析 | |
objectFactory = (type == ExtensionFactory.class ? null : | |
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); | |
} |
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
public T getExtension(String name) {if ("true".equals(name)) {return getDefaultExtension(); | |
} | |
// Holder 也是用于持有对象的, 用作缓存 | |
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) {// 创建拓展实例, 下面分析 createExtension(name) | |
instance = createExtension(name); | |
holder.set(instance); | |
} | |
} | |
} | |
return (T) instance; | |
} |
private T createExtension(String name) { | |
// 从配置文件中加载所有的拓展类, 可得到“配置项名称”到“配置类”的映射关系表 | |
// 加载完后根据 name 获取, 得到形如 com.alibaba.dubbo.demo.provider.spi.OptimusPrime | |
// 待会重点分析 getExtensionClasses(), 删去了一些非关键代码 | |
Class<?> clazz = getExtensionClasses().get(name); | |
try { | |
// 也是尝试先从缓存获取, 获取不到通过反射创建一个并放到缓存中 | |
T instance = (T) EXTENSION_INSTANCES.get(clazz); | |
if (instance == null) {EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); | |
instance = (T) EXTENSION_INSTANCES.get(clazz); | |
} | |
// 依赖注入和 cachedWrapperClasses, 后面分析 | |
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; | |
} | |
} |
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; | |
} |
private Map<String, Class<?>> loadExtensionClasses() { | |
// 获取 SPI 注解, 这里的 type 是在调用 getExtensionLoader 方法时传入的 | |
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) {// 抛异常} | |
// 获取 @SPI 注解中的值, 并缓存起来, 可以关注下, 后面会用到 | |
if (names.length == 1) cachedDefaultName = names[0]; | |
} | |
} | |
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); | |
// 加载指定文件夹下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/ | |
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); | |
// 我是放在这个目录下的 | |
loadDirectory(extensionClasses, DUBBO_DIRECTORY); | |
loadDirectory(extensionClasses, SERVICES_DIRECTORY); | |
return extensionClasses; | |
} |
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) { | |
// fileName 是 META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot | |
String fileName = dir + type.getName(); | |
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()) { | |
// 一个 resourceURL 就是一个文件 | |
java.net.URL resourceURL = urls.nextElement(); | |
loadResource(extensionClasses, classLoader, resourceURL); | |
} | |
} | |
} | |
} |
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, | |
java.net.URL resourceURL) { | |
try { | |
BufferedReader reader = | |
new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8")); | |
try { | |
String line; | |
// 按行读取配置内容 | |
while ((line = reader.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) { | |
// 真正的去加载类 | |
loadClass(extensionClasses, resourceURL, | |
Class.forName(line, true, classLoader), name); | |
} | |
} | |
} | |
} | |
} | |
} | |
} |
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, | |
Class<?> clazz, String name) throws NoSuchMethodException { | |
// clazz 必须是 type 类型的, 否则抛异常 | |
if (!type.isAssignableFrom(clazz)) { } | |
// 判断 clazz 是否为标注了 @Adaptive 注解, 后面分析 | |
if (clazz.isAnnotationPresent(Adaptive.class)) {if (cachedAdaptiveClass == null) {cachedAdaptiveClass = clazz;} else if (!cachedAdaptiveClass.equals(clazz)) {// 抛异常, 不能有多个标注有 @Adaptive 注解的类} | |
} | |
// 判断是否是 Wrapper 类型, 后面分析 | |
else if (isWrapperClass(clazz)) { | |
Set<Class<?>> wrappers = cachedWrapperClasses; | |
if (wrappers == null) {cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); | |
wrappers = cachedWrapperClasses; | |
} | |
wrappers.add(clazz); | |
} | |
// 程序进入此分支, 表明 clazz 是一个普通的拓展类,Robot 就是一个普通的拓展类 | |
else { | |
// 检测 clazz 是否有默认的构造方法, 如果没有, 则抛出异常 | |
clazz.getConstructor(); | |
if (name == null || name.length() == 0) {name = findAnnotationName(clazz); | |
if (name.length() == 0) {// 抛异常} | |
} | |
String[] names = NAME_SEPARATOR.split(name); | |
if (names != null && names.length > 0) {Activate activate = clazz.getAnnotation(Activate.class); | |
if (activate != null) {cachedActivates.put(names[0], activate); | |
} | |
for (String n : names) {if (!cachedNames.containsKey(clazz)) {cachedNames.put(clazz, n); | |
} | |
Class<?> c = extensionClasses.get(n); | |
if (c == null) { | |
// 存储名称到 class 的映射关系, 这样就解析好了一行 | |
extensionClasses.put(n, clazz); | |
} else if (c != clazz) {// 抛异常} | |
} | |
} | |
} | |
} |
正文完