网站首页 > java教程 正文
java篇之SPI详解
Q SPI能做什么?
请先看下面一个业务场景:
框架开发人者-小框:我要开发一款发送消息的框架,为业界的消息发送定义一个标准,具体的发送内容需要交给框架的实现者去做 (类似jdbc,是一个连接数据库的标准)
中间件开发者-小中:最近这款消息框架使用的挺火的,公司要求依据这个框架开发一款发送消息的中间件 (类似mysql的driver,实现如何具体的连接mysql数据库)
后台开发人员-小开:来测试一下公司的消息中间件看看有没有bug (日常的开发,使用jdbc去做mysql数据库连接)
具体实现:
小框:
1、新建maven项目 tool
2、编写接口Msg,定义发送消息的方法
3、 编写发送接口类SendMsg:
好了,剩余的交给实现人员去做
小中:
1、新建maven项目 toolRealize
2、编写实现类 MsgRealize ,做发送消息的具体实现
3、resources下新建目录 META-INF/services,新建文件 com.spi.tool.Msg,文件中写入内容com.coder.toolRealize. MsgRealize
好了,打包上传maven仓库,等别的开发调用
小开:
1、新建maven项目 testSpi
2、引入tool 和 toolRealize
3、新建main方法,测试消息发送
完成!
经过上面的测试,大致能了解SPI能做什么,在我的理解看来,SPI是一种可拔插的实现机制,也类似于策略模式,可以在项目中根据加载不同的jar来动态的实现某些业务。
Q SPI是什么?
SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
02
源码分析
基于JDK1.8分析 ServiceLoader
1、整体分析ServiceLoader的属性
//PREFIX属性代表着我们必须要把实现类的配置文件写在services目录下,属于硬编码内容
private static final String PREFIX = "META-INF/services";
//需要ServiceLoader加载的类的Class对象
private final Class<S> service;
//类加载器,加载配置类
private final ClassLoader loader;
//已经加载的Class的缓存,避免每次都去读配置文件加载
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
//重写了java迭代器,类加载的过程以及实例化都是迭代器完成的,
private LazyIterator lookupIterator;
2、根据调用过程逐步分析源码的执行思路
a)、实际开发中使用SPI的调用流程如下:
ServiceLoader.load(obj.class) // 第一步
.iterator() // 第二步
.hasNext() // 第三步
.next(); // 第四步
b)、思路解析
第一步中执行过程是获取当前类加载器->执行ServiceLoader构造方法->执行reload()方法->清空缓存、实例化迭代器;
第二步获取当前的迭代器对象;
第三步实际执行的是LazyIterator.hasNext() ,然后调用-> hasNextService()
private boolean hasNextService() {
....//省略部分不关键代码
//拼接路径前缀,获取文件的全路径名
String fullName = PREFIX + service.getName();
//读取文件中的所有内容
config = loader.getResources(fullName);
//剩余代码主要就是把文件中写的所有实现类都放置在一个内置的迭代器中,
//防止每次next都去取文件,保证了文件只会最开始读取一次
...//省略部分不关键代码
}
第四步实际执行的是LazyIterator.next(),然后调用 ->nextService()
private S nextService() {
... //省略部分不关键代码
String cn = nextName; //获取当前实现类的全类名,如com.a5.User
nextName = null; //清空当前属性,可以在hasNextService中查询下一个实现类的全类名
Class<?> c = null;
c = Class.forName(cn,false,loader); //根据全类名获取Class对象
... //省略部分不关键代码
S p = service.cast(c.newInstance());//newInstance获取当前类的实例
proproviders.put(cn,p); //放入缓存
return p;//返回实例对象,方法结束
}
3、解析过源码之后我们发现,每次next()都会返回一个文件中定一个类的实例化对象,有了实例化对象,那岂不是可以为所欲为了。
- 上一篇: Python迭代器对象使用
- 下一篇: 通过 JFR 与日志深入探索 JVM - TLAB 原理详解
猜你喜欢
- 2025-01-10 Java 中经常被提到的 SPI 到底是什么?
- 2025-01-10 23种设计模式总结详解(全23种)
- 2025-01-10 Java 中的反应式编程 (RxJava)
- 2025-01-10 Java Stream
- 2025-01-10 推荐给 Java 初学者硬核书籍,亲身经历让你少走弯路
- 2025-01-10 Spring Boot集成AJ-Captcha实现滑动验证码功能
- 2025-01-10 java.util.ArrayList 原理详细介绍
- 2025-01-10 Java ArrayList用法详解附代码示例
- 2025-01-10 Java SPI详解
- 2025-01-10 Qt——容器类
你 发表评论:
欢迎- 04-24Java Collections 工具类集合框架中常用算法解析
- 04-24桶排序的简单理解
- 04-24Java集合框架底层实现原理大揭秘
- 04-24Java 集合框架全面解析:选对数据结构,提升开发效率
- 04-24c#集合排序
- 04-24Java面试中常被问到的集合类深度解读
- 04-24VBA技术资料MF278:对集合进行排序
- 04-24Spring 最常用的 7 大类注解,史上最强整理
- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)