使用测试

前面三篇主要是介绍如何设计的,如何实现的,这一篇,则主要集中在如何使用。实现得再好,如果不好用,也白搭

本篇介绍几个简单的使用case,包括静态使用,动态适配,自定义选择器等

1. 简单的静态使用

定义一个SPI接口 IPrint, 两个实现 FilePrint, ConsolePrint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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

测试代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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);
}
}

输出如下

1
2
3
4
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

演示如下

http://s2.mogucdn.com/mlcdn/c45406/170531_308geabej59hh3hegbf2cdkb0e8kj_1224x718.gif

2. 动态适配

与静态的使用有点区别,主要的区别点在于接口的定义(需要注意第一个参数是作为选择器选择SPI实现的参数),同样是上面这个spi接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@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, 其他的没有啥区别,测试代码如下

1
2
3
4
5
6
7
8
@Test
public void testAdaptivePrint() throws SpiProxyCompileException {
IPrint print = SpiLoader.load(IPrint.class).getAdaptive();


print.adaptivePrint("FilePrint", "[file print]");
print.adaptivePrint("ConsolePrint", "[console print]");
}

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
file adaptivePrint: [file print]
console adaptivePrint: [console print]
```

演示图


![http://s2.mogucdn.com/mlcdn/c45406/170531_54f638fkcl58c6lihl92adei31c78_1222x718.gif](http://s2.mogucdn.com/mlcdn/c45406/170531_54f638fkcl58c6lihl92adei31c78_1222x718.gif)



## 3. 自定义选择器

> 上面两个很简单的演示了下使用方式,最基本的方法, 没有加上 @SpiConf 注解, 没有显示指定选择器类
型,下面则演示下,如何自定义选择器

**SPI接口**

有一个欢迎方法,我们需求根据用户的来源显示不同的欢迎至此, 下面定义了一个 `UserSelector`选择器,这个就是我们自定义的选择器

```java
@Spi
public interface IUser {
@SpiAdaptive(selector = UserSelector.class)
void welcome(UserDO userDO);
}

spi实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

选择器实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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的定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@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;
}
}

测试代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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)

演示如下:

http://s2.mogucdn.com/mlcdn/c45406/170531_8af3ek900d8c783031lc7h375a0b8_1222x718.gif

3. 其他

博客系列链接:

项目: QuickAlarm

个人博客: Z+|blog

基于hexo + github pages搭建的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见识有限,如发现bug或者有更好的建议,随时欢迎批评指正,我的微博地址: 小灰灰Blog

扫描关注

QrCode