手写简易版 Dubbo SPI(极简可运行)
我带你手写一个迷你版 Dubbo SPI,只保留核心底层原理:
- 注解
@SPI - 配置文件加载
- 缓存机制
- 根据 name 获取实现
- IOC 注入(简化版)
- AOP 包装(Wrapper)
代码量不到 300 行,运行即懂底层!
一、先定义核心注解(模拟 Dubbo)
1. @SPI 注解(标识扩展接口)
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SPI {
// 默认扩展名称
String value() default "";
}
2. @Adaptive 注解(自适应方法,简化版)
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Adaptive {
String[] value() default {};
}
二、定义测试接口 + 实现
1. 接口(加 @SPI)
@SPI("human")
public interface HelloService {
void sayHello();
}
2. 实现类 A
public class HumanHello implements HelloService {
@Override
public void sayHello() {
System.out.println("你好,人类!");
}
}
3. 实现类 B
public class RobotHello implements HelloService {
@Override
public void sayHello() {
System.out.println("Hello, Robot!");
}
}
4. AOP 包装类(Wrapper,核心!)
// 包装类:必须持有目标接口对象 + 构造方法传入接口
public class HelloWrapper implements HelloService {
private final HelloService delegate;
public HelloWrapper(HelloService delegate) {
this.delegate = delegate;
}
@Override
public void sayHello() {
System.out.println("===== 前置增强 =====");
delegate.sayHello();
System.out.println("===== 后置增强 =====");
}
}
三、配置文件(固定路径,模拟 Dubbo)
创建文件:
resources/META-INF/dubbo/com.xxx.HelloService
内容:
human=com.xxx.HumanHello
robot=com.xxx.RobotHello
wrapper=com.xxx.HelloWrapper
四、手写核心类:MiniExtensionLoader(灵魂!)
这就是Dubbo ExtensionLoader 简化版,300 行内实现所有核心逻辑。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 手写迷你版 Dubbo ExtensionLoader
* 实现:加载配置、缓存、实例化、IOC、AOP 包装
*/
public class MiniExtensionLoader<T> {
// 1. 缓存:一个接口对应一个 ExtensionLoader
private static final Map<Class<?>, MiniExtensionLoader<?>> LOADER_MAP = new ConcurrentHashMap<>();
// 2. 缓存:name -> 扩展类 Class
private final Map<String, Class<?>> extensionClasses = new ConcurrentHashMap<>();
// 3. 缓存:name -> 扩展实例(最终对象)
private final Map<String, T> extensionInstances = new ConcurrentHashMap<>();
// 扩展接口
private final Class<T> type;
// 默认扩展名称
private String defaultName;
// 固定扫描路径
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
// 私有构造,只能通过 getExtensionLoader 获取
private MiniExtensionLoader(Class<T> type) {
this.type = type;
// 加载 SPI 注解
SPI spi = type.getAnnotation(SPI.class);
if (spi != null) {
this.defaultName = spi.value();
}
// 加载配置文件
loadExtensionClasses();
}
/**
* 获取扩展加载器(入口)
*/
public static <T> MiniExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) throw new IllegalArgumentException("类型不能为空");
if (!type.isInterface()) throw new IllegalArgumentException("必须是接口");
if (!type.isAnnotationPresent(SPI.class)) throw new IllegalArgumentException("必须加 @SPI 注解");
MiniExtensionLoader<T> loader = (MiniExtensionLoader<T>) LOADER_MAP.get(type);
if (loader == null) {
LOADER_MAP.putIfAbsent(type, new MiniExtensionLoader<>(type));
loader = (MiniExtensionLoader<T>) LOADER_MAP.get(type);
}
return loader;
}
/**
* 根据 name 获取扩展实例(核心方法)
*/
public T getExtension(String name) {
if (name == null || name.trim().isEmpty()) throw new IllegalArgumentException("名称不能为空");
// 先查缓存
T instance = extensionInstances.get(name);
if (instance == null) {
instance = createExtension(name);
extensionInstances.put(name, instance);
}
return instance;
}
/**
* 获取默认实例
*/
public T getDefaultExtension() {
return getExtension(defaultName);
}
/**
* 创建扩展实例:实例化 -> IOC 注入 -> AOP 包装
*/
private T createExtension(String name) {
Class<?> clazz = extensionClasses.get(name);
if (clazz == null) throw new IllegalArgumentException("未找到扩展:" + name);
try {
// 1. 实例化
T instance = (T) clazz.newInstance();
// 2. IOC 依赖注入(简化版)
injectExtension(instance);
// 3. AOP 包装(包装类增强)
instance = wrapExtension(instance);
return instance;
} catch (Exception e) {
throw new RuntimeException("创建实例失败", e);
}
}
/**
* 加载配置文件
*/
private void loadExtensionClasses() {
String fileName = DUBBO_DIRECTORY + type.getName();
try {
Enumeration<URL> urls = getClass().getClassLoader().getResources(fileName);
if (urls != null) {
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) {
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith("#")) continue;
// 解析 key=value
String[] kv = line.split("=");
String name = kv[0].trim();
String className = kv[1].trim();
Class<?> clazz = Class.forName(className);
extensionClasses.put(name, clazz);
}
}
}
}
} catch (Exception e) {
throw new RuntimeException("加载配置失败", e);
}
}
/**
* IOC 注入(简化:模拟自动装配)
*/
private void injectExtension(T instance) {
// 真实 Dubbo 扫描 set 方法,这里简化
// 你可以在这里写注入逻辑
}
/**
* AOP 包装(核心:找到所有包装类,层层包装)
*/
private T wrapExtension(T instance) {
Class<?> clazz = instance.getClass();
List<Class<?>> wrappers = new ArrayList<>();
// 找到所有包装类(构造方法参数是当前接口)
for (Class<?> c : extensionClasses.values()) {
try {
c.getConstructor(type);
wrappers.add(c);
} catch (NoSuchMethodException e) {
// 不是包装类,跳过
}
}
// 层层包装
for (Class<?> wrapper : wrappers) {
try {
Constructor<?> constructor = wrapper.getConstructor(type);
instance = (T) constructor.newInstance(instance);
} catch (Exception e) {
throw new RuntimeException("包装失败", e);
}
}
return instance;
}
}
五、测试运行(验证效果)
public class TestMiniSPI {
public static void main(String[] args) {
// 1. 获取扩展加载器
MiniExtensionLoader<HelloService> loader = MiniExtensionLoader.getExtensionLoader(HelloService.class);
// 2. 获取默认实现(human)
HelloService human = loader.getDefaultExtension();
human.sayHello();
System.out.println("------------------------");
// 3. 获取指定实现(robot)
HelloService robot = loader.getExtension("robot");
robot.sayHello();
}
}
运行结果
===== 前置增强 =====
你好,人类!
===== 后置增强 =====
------------------------
===== 前置增强 =====
Hello, Robot!
===== 后置增强 =====
✅ 完美实现 Dubbo SPI 核心:
- 配置加载
- 按 name 获取
- 多级缓存
- AOP 包装增强
- 默认实现
六、手写版对应 Dubbo 底层原理
| 手写代码 | Dubbo 真实源码 | 作用 |
|---|---|---|
@SPI |
@SPI |
标识扩展接口 + 默认值 |
MiniExtensionLoader |
ExtensionLoader |
核心加载器 |
LOADER_MAP |
EXTENSION_LOADERS |
接口 → 加载器缓存 |
extensionClasses |
cachedClasses |
name → Class 缓存 |
extensionInstances |
cachedInstances |
name → 实例缓存 |
loadExtensionClasses() |
loadClass() |
扫描加载配置 |
createExtension() |
createExtension() |
实例化+注入+包装 |
wrapExtension() |
wrap() |
AOP 包装增强 |
META-INF/dubbo/ |
相同路径 | 扩展配置文件 |
七、你能从手写版学到的核心
- Dubbo SPI 本质就是:配置 + 反射 + 缓存 + 包装
- 包装类(Wrapper)就是 AOP 的底层实现
- 一个接口一个 Loader,全缓存保证高性能
- getExtension(name) 流程:缓存 → 实例化 → 注入 → 包装
总结
这份手写极简版 Dubbo SPI,完全还原了Dubbo SPI 底层核心逻辑,去掉了复杂的异常处理、自适应代码,只保留最精髓的 300 行代码,看懂它 = 彻底懂 Dubbo SPI 底层。
百流积聚,江河是也;文若化风,可以砾石。

浙公网安备 33010602011771号