JAV spi 和扩展方式
SPI 机制(Service Provider Interface)其实源自服务提供者框架(Service Provider Framework,参考【EffectiveJava】page6),是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。引入服务提供者就是引入了 spi 接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔
在开发中,有很多地方都适用到了 java 的 spi 机制;例如:JSBC设计
…等,这里我们不去研究这些示例,直接看一下他的实现方式
一、JAVA SPI
其实 java
的 spi
核心核心就是 ServiceLoader
这个类;我们可以通过在 resource/META-INF/services
下,放置要实例化的类实现的统一接口全路径的文件,在里面每行写上实例化类的全路径,即可使用 spi
的方式实例化,如下:
首先定义一个接口:
package com.spi.test.source;public interface Developer {void hello();
}
然后实现两个实现类:
package com.spi.test.source;public class Make1Developer implements Developer {public void hello() {System.out.println("make1 的 develop");}
}public class Make2Developer implements Developer {public void hello() {System.out.println("make2 的 develop");}
}
然后在 resource/META-INF/services
下创建 com.spi.test.source.Developer
文件内容如下:
com.spi.test.source.Make1Developer
com.spi.test.source.Make2Developer
测试类如下:
public static void main(String[] args) {ServiceLoader<Developer> serviceLoader = ServiceLoader.load(Developer.class);// 循环调用下实例的方法serviceLoader.forEach(item -> {System.out.println("正在执行的类是:" + item.getClass().toString());item.hello();});
}
二、扩展 java spi
上线是 jdk
自己携带的 SPI
功能,但是他设计的并不太好,只实现了基础的功能;另外 dubbo
对 SPI
也有自己的扩展方式,而且实现上也有了优化,
dubbo spi
对比 java spi
的优化点:
- JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源
- 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点
- 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因
有兴趣可以去看一下 dubbo-common.jar
下的 org.apache.dubbo.common.extension.ExtensionLoader
类的实现;这里不在详细说明,我们这里只简单模仿 java spi
自己扩展一个 spi
的功能
首先定义一个注解,这里我们加一步校验,就是接口上必须加上注解,才可以将实现其接口的类实例化:
package com.spi.test.my.source;import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SpiActive {}
然后自己实现一个扩展类加载器辅助类:
package com.spi.test.my.source;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;import static java.nio.charset.StandardCharsets.UTF_8;public class ExpansionLoader<T> implements Iterable<T> {/*** 约定第三方实现配置文件目录**/private static final String SERVICE_DIRECTORY = "META-INF/expansions/";/*** 接口的类型,用于获取此接口下的第三方实现**/private final Class<T> type;/*** 实例列表,用于保存获取到的第三方实现的列表**/private Set<T> targets;public ExpansionLoader(Class<T> type) {this.type = type;this.targets = this.loadExtensionFile();}/*** 工厂方法,用于通过接口获取第三方实现的类加载器** @param type 接口的类型* @return 返回一个指定接口类型的类加载器辅助类**/public static <T> ExpansionLoader<T> load(Class<T> type) {if (type == null) {throw new IllegalArgumentException("Spi需要知道你想要找到哪个功能的第三方实现!");}if (!type.isInterface()) {throw new IllegalArgumentException("只支持寻找接口类型的第三方实现!");}if (type.getAnnotation(SpiActive.class) == null) {throw new IllegalArgumentException("目标接口必须被@Spi注解标注!");}return new ExpansionLoader<>(type);}private Set<T> loadExtensionFile() {String fileName = ExpansionLoader.SERVICE_DIRECTORY + this.type.getName();try {ClassLoader classLoader = ExpansionLoader.class.getClassLoader();Enumeration<URL> urls = classLoader.getResources(fileName);if (urls == null) {return Collections.emptySet();}URL resourceUrl = urls.nextElement();return loadResource(classLoader, resourceUrl);} catch (IOException e) {return Collections.emptySet();}}private Set<T> loadResource(ClassLoader classLoader, URL resourceUrl) {Set<T> set = new HashSet<>();try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceUrl.openStream(), UTF_8))) {String line;while ((line = reader.readLine()) != null) {final int ci = line.indexOf('#');if (ci == 0) {continue;} else if (ci > 0) {line = line.substring(0, ci);}Class<T> clazz = (Class<T>) classLoader.loadClass(line.trim());set.add(clazz.newInstance());}} catch (Exception e) {}return set;}@Overridepublic Iterator<T> iterator() {return this.targets.iterator();}
}
以上是工具部分;下面开始定义第三方需要实现的接口:
package com.spi.test.my;import com.spi.test.my.source.SpiActive;@SpiActive
public interface Production {void hello();
}
然后添加两个实现类:
package com.spi.test.my;public class Make1Production implements Production {@Overridepublic void hello() {System.out.println("make1 的 Production");}
}public class Make2Production implements Production {@Overridepublic void hello() {System.out.println("make2 的 Production");}
}
然后在 resource/META-INF/expansions
下创建 com.spi.test.my.Production
文件内容如下:
com.spi.test.my.Make1Production
com.spi.test.my.Make2Production
测试类如下:
public static void main(String[] args) {ExpansionLoader<Production> extensionLoader = ExpansionLoader.load(Production.class);extensionLoader.forEach(item -> {System.out.println("正在执行的类是:" + item.getClass().toString());item.hello();});
}
JAV spi 和扩展方式相关推荐
- SQL与NoSQL区别-扩展方式
扩展方式是NoSQL数据库与关系型数据库差别最大的地方. 由于关系型数据库将数据存储在数据表中,数据操作的瓶颈出现在多张数据表的操作中,而且数据表越多这个问题越严重,如果要缓解这个问题,只能提高处理能 ...
- 为operamasks增加HTML扩展方式的组件调用
#为operamasks增加HTML扩展方式的组件调用##背景 之前的[博文](http://www.cnblogs.com/p2227/p/3540858.html)中有提及到,发现easyui中的 ...
- php curl https_PHP FFI:一种全新的PHP扩展方式
(给PHP开发者加星标,提升PHP技能) 转自:laruence/鸟哥 www.laruence.com/2020/03/11/5475.html 随着 PHP7.4 而来的有一个我认为非常有用的一个 ...
- 常见的数据扩展方式unsqueeze与expand的用法与区别
常见的数据扩展方式unsqueeze与expand的用法与区别 unsqueeze以及expand的区别 unsqueeze以及expand的区别 unsqueeze可以增加一个维度,但是维度的siz ...
- 系统扩展方式 scale up和scale out
什么是scale up和scale out? 许多存储系统开始很简单,但当需要进行系统扩展时就会变得复杂.升级存储系统最常见的原因是需要更多的容量,以支持更多的用户,文件,应用程序或连接的服务器. 但 ...
- 系统扩展方式 scale up和scale out(转载)
什么是scale up和scale out? 许多存储系统开始很简单,但当需要进行系统扩展时就会变得复杂.升级存储系统最常见的原因是需要更多的容量,以支持更多的用户,文件,应用程序或连接的服务器. 但 ...
- [亲测失败] Ubuntu 双显示器扩展方式 接显示器分辨率低的解决 [拼接整合]
Ubuntu 10.04双显示器扩展方式,笔记本外接显示器分辨率低增加没有的分辨率. 使用扩展方式,但是外接的显示器没有认出来(未知),分辨率超不过笔记本的1366x768,原本外接显示器是19寸宽屏 ...
- 《ArcGIS Runtime SDK for Android开发笔记》——(13)、图层扩展方式加载Google地图...
1.前言 http://mt2.google.cn/vt/lyrs=m@225000000&hl=zh-CN&gl=cn&x=420&y=193&z=9& ...
- spi的dma方式前四个字节_UTF-16是固定两个字节长度吗?
答案是否定的. Unicode 和 UTF-8 有什么区别?www.zhihu.com 介绍两个概念,字符集和编码规则. Unicode 是「字符集」 UTF-8 是「编码规则」 字符集:为每一个「 ...
最新文章
- SQLite与pandas
- XShell连接Deepin
- 虚幻4皮肤材质_虚幻周报20200721 | CJ就要开始啦~
- CATALAN数 学习
- python回归方程系数计算_线性回归中的正规方程将θ系数返回为“NaN”
- ZOJ - 2972 Hurdles of 110m(记忆化搜索/动态规划)
- 程序员修神之路--略懂数据库集群读写分离而已
- jzoj3055-比赛【数学,统计】
- 关于codeforces加载慢
- 带有Python示例的math.exp()方法
- CLIP再创辉煌!西南交大MSRA提出CLIP4Clip,进行端到端的视频文本检索!
- [转载]了解Linux的进程与线程
- 安卓学习笔记17:常用控件 - 编辑框
- h5跳转小程序页面url_小程序和h5跳转
- 【论文笔记】Recover Canonical-View Faces in the Wild with Deep Neural Network
- Hindex--华为Hbase二级索引
- android 常用依赖库
- 数学知识——博弈论(巴什博奕、尼姆博奕、威佐夫博奕)思路及例题
- 易语言禁止服务器,禁止指定程序联网易语言源码
- linux命令测网速
热门文章
- 头条php,基于PHP的免费新闻头条接口查询
- 笔记本电源指示灯维修及散热硅脂替换
- 那年杏花微雨,你说你是BOM
- Genymotion 各对应版本
- Java显示棋盘_Java的GUI学习:显示国际棋盘
- C语言编程规范 学习笔记
- 综合实践计算机的入门知识教学设计,3-6年级综合实践活动3.我是电脑小画家_教案、教学设计_市级优课(0001)【信息技术】.doc...
- 监听通知栏内容,获取通知栏消息,安卓原生SDK扩展
- 开源在线excel编辑器_推荐3款爽到爆的在线网站
- 基于Springboot的在线租车,自租车,企业租车管理系统,基于Idea开发