SPI框架實現之旅四:使用測試

SPI框架實現之旅四:使用測試

前面三篇主要是介紹如何設計的,如何實現的,這一篇,則主要集中在如何使用。實現得再好,若是很差用,也白搭java

本篇介紹幾個簡單的使用case,包括靜態使用,動態適配,自定義選擇器等git

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, 內容以下app

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);
   }
}

輸出以下ide

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接口ui

@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, 其餘的沒有啥區別,測試代碼以下this

@Test
public void testAdaptivePrint() throws SpiProxyCompileException {
  IPrint print = SpiLoader.load(IPrint.class).getAdaptive();


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

輸出結果.net

file adaptivePrint: [file print]
console adaptivePrint: [console print]

演示圖

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

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)

演示以下:

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

4. 其餘

博客系列連接:

源碼地址:

https://git.oschina.net/liuyueyi/quicksilver/tree/master/silver-spi

相關文章
相關標籤/搜索