一、前戲-- 功能需求
通過上一篇對soul插件鏈的整體概覽學習,本篇開始“生搬硬套”,盡量符合soul的編碼方式實現一個自定義單一職責插件。
需求:實現一個參數驗簽的插件,驗簽失敗中斷調用鏈調用直接返回錯誤信息,否則執行剩下調用鏈。
二、自定義插件類
- 在soul-plugin項目下,新建一個子模塊soul-plugin-customsign。創建SoulPlugin接口的實現類CustomSignPlugin。
- 實現SoulPlugin接口定義方法,各方法職責如下:
execute()
方法為核心的執行方法,用戶可以在里面自由的實現自己想要的功能。
getOrder()
指定插件的排序。
named()
指定插件的名稱。
skip()
在特定的條件下,該插件是否被跳過。
此處附上一個小demo:
@Slf4j
public class CustomSignPlugin implements SoulPlugin {
/**
* 加密私鑰
*/
private String privateKey = "CDRjzk2sb99v6nUkXx8+6g";
@Override
public String named() {
return PluginEnum.CUSTOMSIGN.getName();
}
@Override
public Boolean skip(final ServerWebExchange exchange) {
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
return !Objects.equals(Objects.requireNonNull(soulContext).getRpcType(), RpcTypeEnum.HTTP.getName());
}
@Override
public Mono<Void> execute(ServerWebExchange exchange, SoulPluginChain chain) {
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
String queryParam = exchange.getRequest().getURI().getQuery();
if (StringUtils.isNotBlank(queryParam)) {
if(!checkSignature(exchange.getRequest())) {
log.error("接口請求驗簽失敗");
Object error = SoulResultWrap.error(SoulResultEnum.CHECK_CUSTOM_SIGN_EXCEPTION.getCode(), SoulResultEnum.CHECK_CUSTOM_SIGN_EXCEPTION.getMsg(), null);
return WebFluxResultUtils.result(exchange, error);
}
}
return chain.execute(exchange);
}
private boolean checkSignature(ServerHttpRequest request) {
Map<String, String> map = request.getQueryParams().toSingleValueMap();
String signReq = map.containsKey("sign")? map.get("sign").toString() : null;;
boolean checkSignture = false;
//驗證簽名
if (map.size() > 0 && StringUtils.isNotBlank(signReq)) {
//簽名匹配,則簽名認證通過
map.remove("sign");
if (signReq.equals(Signature.getSign(map, privateKey))
|| signReq.equalsIgnoreCase(Signature.getSign(map, privateKey))) {
checkSignture = true;
}
} else {
// 不傳簽名,非法強求
checkSignture = false;
}
return checkSignture;
}
@Override
public int getOrder() {
return PluginEnum.CUSTOMSIGN.getCode();
}
- soul-plugin-customsign目錄結構如下:
image.png
- 調整pom, install 本地倉庫,待引用
三、設置插件執行順序
在PluginEnum類中創建,自定義插件的執行順序,名字等信息。
ps:1.枚舉中code定義相對松散,這個間隔目前懷疑是為了便于“滲入”新插件
image.png
2.枚舉類中的name一定要定義好且具有唯一性,后文配置中還會用到
四、設置異常
SoulResultEnum類中定義統一錯誤異常碼,此處建議使用英文,此處用中文作以區分。
image.png
五、封裝spring-starter
- 在soul-spring-boot-starter-plugin項目下,創建子模塊soul-spring-boot-starter-plugin-customsign
- 引入自定義插件依賴
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-plugin-customsign</artifactId>
<version>${project.version}</version>
</dependency>
- 此類只是對做一個簡單的spring-starter封裝,封裝一個CustomSignPluginConfiguration
@Configuration
public class CustomSignPluginConfiguration {
/**
* init SoulPlugin.
*
* @return {@linkplain SoulPlugin}
*/
@Bean
public SoulPlugin customSignPlugin() {
return new CustomSignPlugin();
}
}
- 當然不要忘記加上加載bean的配偶spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.dromara.soul.spring.boot.plugin.divide.DividePluginConfiguration
- 整體目錄結構如下
image.png
六、soul-bootstrap的配置
此處配置就“過于”簡單,只需要引入maven依賴即可,體現出作者對代碼的高內聚、低耦合了,nice!
<pre><dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-plugin-customsign</artifactId>
<version>${project.version}</version>
</dependency></pre>
七、配置插件信息
- 整體項目mvn一下
mvn clean package install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Drat.skip=true -Dcheckstyle.skip=true
- 插件管理中配置插件信息,此處插件名正式前文PluginEnum中定義name,一定保持一致否則插件鏈初始化將失敗,慘痛浪費15分鐘調試經驗。
image.png
八、測試
- 分別啟動soul-bootstrap、soul-plugin-customsign測試一下
image.png
一發入魂,搞定! 收拾收拾可以再睡一會了。
九、小結
- 知易行難:感覺自己捋清了調用鏈,實現一個自定義插件應該還算easy。實際操作起來,其實~~~~我還是很菜啊!
- 回顧整個實現流程,確實很精巧。熱插拔的插件鏈,確實很精巧,原有代碼基本不需要改動,確實高內聚低耦合。確實很精巧!!!
- 日拱一卒,每天進步一點點