六、SPI机制 —— 框架的扩展灵魂
SPI(Service Provider Interface)是Java内置的服务发现机制,也是Dubbo、JDBC等框架实现可插拔架构的基础。
6.1 Java原生SPI
// 1. 定义接口
public interface Logger {
void log(String msg);
}
// 2. 实现
public class ConsoleLogger implements Logger {
public void log(String msg) { System.out.println("Console:" + msg); }
}
// 3. 在META-INF/services/com.example.Logger文件中写入:
// com.example.ConsoleLogger
// 4. 使用ServiceLoader加载
ServiceLoader<Logger> loader = ServiceLoader.load(Logger.class);
for (Logger logger : loader) {
logger.log("Hello SPI"); // 输出: Console:Hello SPI
}
6.2 Dubbo的扩展点机制(增强SPI)
Dubbo对原生SPI做了增强,增加了自适应扩展和按需激活等功能:
@SPI("dubbo") // 默认扩展名为dubbo
public interface Protocol {
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
}
// 配置文件:META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
使用ExtensionLoader获取指定扩展:
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("http");
七、如何高效阅读源码?(方法论)
很多开发者说“源码太难了,根本看不懂”。这里分享一套可落地的方法:
7.1 步骤一:宏观定骨架
不要一上来就逐行读。先画核心类图和调用时序图。例如读Spring refresh(),先画出它的10个步骤,理解每一步“做了什么”而不是“怎么做的”。
7.2 步骤二:带着问题阅读
例如:
“Spring事务是如何传播的?”
“Mybatis的Mapper接口为什么没有实现类也能调用?”
带着具体问题去源码中找答案,效率远高于通读。
7.3 步骤三:写注释 + 调试
在IDE中下载源码,在关键方法上写中文注释。单步调试时,观察变量的值变化,尤其是beanDefinitionMap、singletonObjects这类核心集合。
7.4 步骤四:对比阅读
对比相似组件的实现,可以加深理解。例如:
JdkDynamicAopProxy vs CglibAopProxy
Mybatis的Executor(SimpleExecutor/ReuseExecutor/BatchExecutor)
7.5 步骤五:输出倒逼输入
尝试用1000字以内的篇幅解释一个模块原理,或画一张流程图,甚至造一个极简版“轮子”。输出过程中你会发现自己哪里没真正理解。
八、进阶实战:模拟Spring实现简易IoC容器
为了检验对IoC的理解,让我们动手实现一个超轻量级容器(200行内):
public class SimpleIoC {
// 存储单例Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 存储Bean定义
private final Map<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
// 注册Bean定义
public void registerBean(Class<?> clazz) {
BeanDefinition bd = new BeanDefinition();
bd.setBeanClass(clazz);
bd.setScope("singleton");
beanDefinitions.put(clazz.getSimpleName(), bd);
}
// 初始化所有单例
public void refresh() {
for (Map.Entry<String, BeanDefinition> entry : beanDefinitions.entrySet()) {
if ("singleton".equals(entry.getValue().getScope())) {
Object bean = createBean(entry.getValue());
singletonObjects.put(entry.getKey(), bean);
}
}
// 执行依赖注入(简单起见只处理@Autowired字段)
doDependencyInjection();
}
private Object createBean(BeanDefinition bd) {
try {
return bd.getBeanClass().getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("创建Bean失败", e);
}
}
private void doDependencyInjection() {
for (Object bean : singletonObjects.values()) {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
field.setAccessible(true);
Object dependency = singletonObjects.get(field.getType().getSimpleName());
try {
field.set(bean, dependency);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
public <T> T getBean(Class<T> clazz) {
return (T) singletonObjects.get(clazz.getSimpleName());
}
static class BeanDefinition {
private Class<?> beanClass;
private String scope;
// getters/setters
}
}
这个简易容器虽然简陋,但包含了注册定义、实例化、依赖注入三个核心环节,足以说明IoC的本质。
当你能够用框架的思想去分析问题,用源码的逻辑去预测行为,甚至能基于框架进行二次开发或贡献补丁时,你已经从“框架的使用者”成长为“框架的驾驭者”。这不仅是你技术能力的跃迁,更是职业发展中的关键里程碑。
来源:
http://vhjpe.cn/