Mybatis中攔截器的使用場(chǎng)景和技巧分享
場(chǎng)景描述
中小業(yè)務(wù)系統(tǒng)中,有時(shí)候會(huì)面臨一些比較相似的業(yè)務(wù)場(chǎng)景。
- 當(dāng)XXX創(chuàng)建成功后,需要給XXX推送一條業(yè)務(wù)消息;
- 當(dāng)XXX更新的時(shí)候,需要給XXX推送一條業(yè)務(wù)提醒;
- 當(dāng)XXX創(chuàng)建OR更新的時(shí)候,需要將最新的數(shù)據(jù)推送到搜索引擎中,以方便其他業(yè)務(wù)系統(tǒng)在搜索引擎中能查詢到;
- 當(dāng)XXX更新的時(shí)候,需要更新分布式緩存的XXX數(shù)據(jù);
- ……
這些場(chǎng)景都有相似的特征,如下
| 特征 | 描述 |
|---|---|
| 異步 | 實(shí)際上與主流程關(guān)系不大,可以作為附屬流程異步執(zhí)行 |
| 事務(wù) | 大多在事務(wù)結(jié)束之后執(zhí)行 |
| 并行 | 可以串行執(zhí)行,也可以并行執(zhí)行,對(duì)最終結(jié)果影響不大 |
Mybatis攔截器
Mybatis提供了一些機(jī)制,可以允許我們?cè)谧鰯?shù)據(jù)庫操作的時(shí)候進(jìn)行我們額外的一些程序。當(dāng)然,這看起來并沒有JPA的EntityListener好用。
但是通過這些機(jī)制,我們可以達(dá)成我們主要的目的,解耦合。(解耦合的好處顯而易見,我們?cè)陉P(guān)注主流程業(yè)務(wù)的時(shí)候,附屬流程的業(yè)務(wù)也更容易擴(kuò)展)
下面是我們即將用到的一些簡(jiǎn)單概念,如下
| 概念 | 描述 |
|---|---|
| 解耦 | 解耦的本質(zhì)就是將類之間的直接關(guān)系轉(zhuǎn)換成間接關(guān)系 |
| 觀察者模式 | 一種行為設(shè)計(jì)模式,允許你定義一種訂閱機(jī)制,可在對(duì)象事件發(fā)生時(shí)通知多個(gè)“觀察”該對(duì)象的其他對(duì)象。 |
| EventBus | 基于事件驅(qū)動(dòng),觀察者們監(jiān)聽自己感興趣的特定事件,進(jìn)行相應(yīng)的處理。 |
錨定事件發(fā)生
首先,我們要找到事件發(fā)生的地方,通常來說我們更愿意在業(yè)務(wù)代碼里寫
Company company = new Company(); company.setCompanyName(companyName); companyMapper.insert(company); // 創(chuàng)建XXX成功后,觸發(fā)了一些操作; doSomething(company);
但是,這種方式是耦合的,我們?cè)?strong>doSomething() 里發(fā)生的一些異常會(huì)影響到主流程,而每一次增加附屬流程的改動(dòng),都會(huì)產(chǎn)生影響范圍的蔓延。
這里,我們使用Mybatis提供的一種方式來錨定事件發(fā)生
@Component
@Intercepts( {
@Signature(method = "update", type = Executor.class, args = {
MappedStatement.class,
Object.class
})
})
public class EntityInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(EntityInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object proceed = invocation.proceed();
try {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object params = args[1];
if (SqlCommandType.INSERT.equals(ms.getSqlCommandType())) {
insertCommond(params);
}
if (SqlCommandType.UPDATE.equals(ms.getSqlCommandType())) {
updateCommond(params);
}
} catch (Exception e) {
LOGGER.warn("entity change warning: {}", e.getMessage());
} finally {
return proceed;
}
}
private void insertCommond(Object params) {
if (Objects.isNull(params)) {
return;
}
// 插入Company的事件
if (params instanceof Company) {
Company company = (Company) params;
CompanyEvent companyEvent =
new CompanyEvent(CompanyEvent.TOPIC_ADD, WebUtils.getOpenId(), CompanyVo.from(company));
SpringUtil.getApplicationContext().publishEvent(companyEvent);
LOGGER.info("[發(fā)布事件:{}] - [事件內(nèi)容:{}]", CompanyEvent.TOPIC_ADD, JSON.toJSONString(companyEvent));
}
}
}
在這里,我們監(jiān)聽了Mybatis的Executor對(duì)象的update方法,來監(jiān)聽對(duì)象的新增和修改。
package org.apache.ibatis.executor;
public interface Executor {
int update(MappedStatement ms, Object parameter) throws SQLException;
}
當(dāng) Company 的 insert 事件發(fā)生時(shí),我們發(fā)布了一條 CompanyEvent 的事件。
訂閱事件
我們使用發(fā)布-訂閱模型實(shí)現(xiàn)了解耦合,針對(duì)剛才發(fā)布的 CompanyEvent 事件,我們來寫一個(gè)事件消費(fèi)者。
@Component
public class CompanyListener {
/**
* 監(jiān)聽事件 - 公司.
*
* @param companyEvent 事件.
*/
@Lazy
@Async
@TransactionalEventListener(
fallbackExecution = true,
phase = TransactionPhase.AFTER_COMPLETION,
classes = CompanyEvent.class)
public void doAsync(CompanyEvent companyEvent) {
LOGGER.info("[Company 訂閱:{}] - [開始執(zhí)行:{}]",
companyEvent.getTopic(), JSON.toJSONString(companyVo));
}
}
代碼中涉及到一些注解,簡(jiǎn)單介紹下
| 注解 | 介紹 |
|---|---|
| @Component | 會(huì)注冊(cè)為Spring的一個(gè)Bean |
| @Lazy | 該方法會(huì)異步執(zhí)行 |
| @TransactionalEventListener | 在事務(wù)的不同階段去觸發(fā)執(zhí)行該監(jiān)聽 |
我們標(biāo)注了該事件是在TransactionPhase.AFTER_COMPLETION(事務(wù)提交完成)這個(gè)事務(wù)節(jié)點(diǎn)進(jìn)行事件處理。
觀察者模式
上面使用了Spring的EventListener來實(shí)現(xiàn)的事件驅(qū)動(dòng),除此之外,還可以使用Guava的EventBus、Vert.x的EventBus等方式實(shí)現(xiàn)。
很多工具類提供了關(guān)于EventBus的實(shí)現(xiàn),但是使用邏輯和方式上大多大同小異。
總結(jié)
觀察者模式、監(jiān)聽模式都有利于解耦合,根據(jù)業(yè)務(wù)訴求合理的進(jìn)行開發(fā)設(shè)計(jì),能為以后的擴(kuò)展打下堅(jiān)實(shí)的基礎(chǔ)。
以上就是Mybatis中攔截器的使用場(chǎng)景和技巧分享的詳細(xì)內(nèi)容,更多關(guān)于Mybatis攔截器使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中字符串轉(zhuǎn)int數(shù)據(jù)類型的三種方式
這篇文章主要介紹了Java中字符串轉(zhuǎn)int數(shù)據(jù)類型的三種方式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
SpringBoot配置SwaggerUI訪問404錯(cuò)誤的解決方法
這篇文章主要為大家詳細(xì)介紹了SpringBoot配置SwaggerUI訪問404錯(cuò)誤的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
java中Sources目錄Resources目錄的區(qū)別解讀
這篇文章主要介紹了java中Sources目錄Resources目錄的區(qū)別解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12

