SPI框架实现之旅四:使用测试
时间:2022-04-27
本文章向大家介绍SPI框架实现之旅四:使用测试,主要内容包括SPI框架实现之旅四:使用测试、2. 动态适配、3. 自定义选择器、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
SPI框架实现之旅四:使用测试
前面三篇主要是介绍如何设计的,如何实现的,这一篇,则主要集中在如何使用。实现得再好,如果不好用,也白搭
本篇介绍几个简单的使用case,包括静态使用,动态适配,自定义选择器等
1. 简单的静态使用
定义一个SPI接口 IPrint
, 两个实现 FilePrint
, ConsolePrint
@Spi
public interface IPrint {
void print(String str);
}
public class FilePrint implements IPrint {
@Override
public void print(String str) {
System.out.println("file print: " + str);
}
}
public class ConsolePrint implements IPrint {
@Override
public void print(String str) {
System.out.println("console print: " + str);
}
}
添加配置文件 com.hust.hui.quicksilver.spi.test.print.IPrint
, 内容如下
com.hust.hui.quicksilver.spi.test.print.ConsolePrint
com.hust.hui.quicksilver.spi.test.print.FilePrint
测试代码如下
@Test
public void testPrint() throws NoSpiMatchException {
SpiLoader<IPrint> spiLoader = SpiLoader.load(IPrint.class);
IPrint print = spiLoader.getService("ConsolePrint");
print.print("console---->");
print = spiLoader.getService("FilePrint");
print.print("file---->");
try {
print = spiLoader.getService("undefine");
print.print("undefine----");
Assert.assertTrue(false);
} catch (Exception e) {
System.out.println("type error-->" + e);
}
try {
print = spiLoader.getService(123);
print.print("type error----");
Assert.assertTrue(false);
} catch (Exception e){
System.out.println("type error-->" + e);
}
}
输出如下
console print: console---->
file print: file---->
type error-->com.hust.hui.quicksilver.spi.exception.NoSpiMatchException: no spiImpl match the name you choose! your choose is: undefine
type error-->java.lang.IllegalArgumentException: conf spiInterfaceType should be sub class of [class java.lang.String] but yours:class java.lang.Integer
演示如下
2. 动态适配
与静态的使用有点区别,主要的区别点在于接口的定义(需要注意第一个参数是作为选择器选择SPI实现的参数),同样是上面这个spi接口
@Spi
public interface IPrint {
void print(String str);
void adaptivePrint(String conf, String str);
}
@Override
public void print(String str) {
System.out.println("file print: " + str);
}
@Override
public void adaptivePrint(String conf, String str) {
System.out.println("file adaptivePrint: " + str);
}
}
public class ConsolePrint implements IPrint {
@Override
public void print(String str) {
System.out.println("console print: " + str);
}
@Override
public void adaptivePrint(String conf, String str) {
System.out.println("console adaptivePrint: " + str);
}
}
主要是新增了一个接口 adaptivePrint
, 其他的没有啥区别,测试代码如下
@Test
public void testAdaptivePrint() throws SpiProxyCompileException {
IPrint print = SpiLoader.load(IPrint.class).getAdaptive();
print.adaptivePrint("FilePrint", "[file print]");
print.adaptivePrint("ConsolePrint", "[console print]");
}
输出结果
file adaptivePrint: [file print]
console adaptivePrint: [console print]
演示图
3. 自定义选择器
上面两个很简单的演示了下使用方式,最基本的方法, 没有加上 @SpiConf 注解, 没有显示指定选择器类 型,下面则演示下,如何自定义选择器
SPI接口
有一个欢迎方法,我们需求根据用户的来源显示不同的欢迎至此, 下面定义了一个 UserSelector
选择器,这个就是我们自定义的选择器
@Spi
public interface IUser {
@SpiAdaptive(selector = UserSelector.class)
void welcome(UserDO userDO);
}
spi实现类
public class QQUser implements IUser {
@Override
public void welcome(UserDO userDO) {
System.out.println("qq 欢迎你! " + userDO);
}
}
public class WeixinUser implements IUser {
@Override
public void welcome(UserDO userDO) {
System.out.println("weixin 欢迎你! " + userDO);
}
}
META-INF/services/
目录下的配置如下 com.hust.hui.quicksilver.spi.def.spi.IUser
com.hust.hui.quicksilver.spi.def.spi.QQUser
com.hust.hui.quicksilver.spi.def.spi.WeixinUser
选择器实现如下
public class UserSelector implements ISelector<UserDO> {
@Override
public <K> K selector(Map<String, SpiImplWrapper<K>> map, UserDO conf) throws NoSpiMatchException {
if (conf == null || conf.getMarket() == null) {
throw new IllegalArgumentException("userDo or userDO#market should not be null!");
}
String name = conf.getMarket().getName();
if (map.containsKey(name)) {
return map.get(name).getSpiImpl();
}
throw new NoSpiMatchException("no spiImp matched marked: " + conf.getMarket());
}
}
从上面的选择器逻辑可以看出,我们是根据 UserDO的market参数来进行选择的, UserDO的定义如下
@Getter
@Setter
@ToString
public class UserDO {
private String uname;
private String avatar;
private MarketEnum market;
}
public enum MarketEnum {
WEIXIN("WeixinUser"),
QQ("QQUser");
private String name;
MarketEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
测试代码如下
@Test
public void testUserSPI() throws SpiProxyCompileException {
SpiLoader<IUser> loader = SpiLoader.load(IUser.class);
IUser user = loader.getAdaptive();
UserDO weixinUser = new UserDO();
weixinUser.setAvatar("weixin.avatar.jpg");
weixinUser.setUname("微信用户");
weixinUser.setMarket(MarketEnum.WEIXIN);
user.welcome(weixinUser);
UserDO qqUser = new UserDO();
qqUser.setAvatar("qq.avatar.jpg");
qqUser.setUname("qq用户");
qqUser.setMarket(MarketEnum.QQ);
user.welcome(qqUser);
System.out.println("-----over------");
}
输出结果:
weixin 欢迎你! UserDO(uname=微信用户, avatar=weixin.avatar.jpg, market=WEIXIN)
qq 欢迎你! UserDO(uname=qq用户, avatar=qq.avatar.jpg, market=QQ)
演示如下:
4. 其他
博客系列链接:
- SPI框架实现之旅一:背景介绍
- SPI框架实现之旅二:整体设计
- SPI框架实现之旅三:实现说明
- SPI框架实现之旅四:使用测试
源码地址:
https://git.oschina.net/liuyueyi/quicksilver/tree/master/silver-spi
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 详解Linux如何生成随机数字和字符串
- linux(ubuntu)用户连续N次输入错误密码进行登陆时自动锁定X分钟
- linux下安装golang的方法
- Linux系统的文件传输方法
- CentOS 6.8 NFS 文件共享设置的方法
- linux如何mount挂载磁盘并设置开机自动mount的实现
- 浅谈Linux的编码及编码转换方法
- 在 Linux 上用 DNS 实现简单的负载均衡的方法
- centos7.2.1511安装jdk1.8.0_151及mysql5.6.38的方法
- Linux CentOS使用crontab设置定时重启的方法
- centos安装php5、卸载php、安装php7的教程
- centos7中crontab定时计划任务5分钟一次命令写法
- Ubuntu16.04 安装Teamviewer的教程详解
- 01 . OpenResty简介部署,优缺点,压测,适用场景及用Lua实现服务灰度发布
- 详解Linux iptables 命令