一梁厉、前戲-- 功能需求
通過上一篇對soul插件鏈的整體概覽學習懊渡,本篇開始“生搬硬套”冠句,盡量符合soul的編碼方式實現(xiàn)一個自定義單一職責插件。
需求:實現(xiàn)一個參數(shù)驗簽的插件织咧,驗簽失敗中斷調(diào)用鏈調(diào)用直接返回錯誤信息胀葱,否則執(zhí)行剩下調(diào)用鏈。
二笙蒙、自定義插件類
- 在soul-plugin項目下抵屿,新建一個子模塊soul-plugin-customsign。創(chuàng)建SoulPlugin接口的實現(xiàn)類CustomSignPlugin捅位。
- 實現(xiàn)SoulPlugin接口定義方法轧葛,各方法職責如下:
execute()
方法為核心的執(zhí)行方法,用戶可以在里面自由的實現(xiàn)自己想要的功能艇搀。
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目錄結(jié)構(gòu)如下:
image.png
- 調(diào)整pom, install 本地倉庫蚜锨,待引用
三档插、設置插件執(zhí)行順序
在PluginEnum類中創(chuàng)建,自定義插件的執(zhí)行順序亚再,名字等信息。
ps:1.枚舉中code定義相對松散,這個間隔目前懷疑是為了便于“滲入”新插件
image.png
2.枚舉類中的name一定要定義好且具有唯一性特笋,后文配置中還會用到
四、設置異常
SoulResultEnum類中定義統(tǒng)一錯誤異常碼耘柱,此處建議使用英文,此處用中文作以區(qū)分棍现。
image.png
五调煎、封裝spring-starter
- 在soul-spring-boot-starter-plugin項目下,創(chuàng)建子模塊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
- 整體目錄結(jié)構(gòu)如下
image.png
六己肮、soul-bootstrap的配置
此處配置就“過于”簡單士袄,只需要引入maven依賴即可,體現(xiàn)出作者對代碼的高內(nèi)聚谎僻、低耦合了娄柳,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分鐘調(diào)試經(jīng)驗需了。
image.png
八、測試
- 分別啟動soul-bootstrap般甲、soul-plugin-customsign測試一下
image.png
一發(fā)入魂肋乍,搞定! 收拾收拾可以再睡一會了敷存。
九墓造、小結(jié)
- 知易行難:感覺自己捋清了調(diào)用鏈,實現(xiàn)一個自定義插件應該還算easy锚烦。實際操作起來觅闽,其實~~~~我還是很菜啊涮俄!
- 回顧整個實現(xiàn)流程蛉拙,確實很精巧。熱插拔的插件鏈彻亲,確實很精巧孕锄,原有代碼基本不需要改動,確實高內(nèi)聚低耦合苞尝。確實很精巧;痢!宙址!
- 日拱一卒轴脐,每天進步一點點