• Mybatis中拦截器的使用场景和技巧


    场景描述

    中小业务系统中,有时候会面临一些比较相似的业务场景。

    1. XXX创建成功后,需要给XXX推送一条业务消息;
    2. XXX更新的时候,需要给XXX推送一条业务提醒;
    3. XXX创建OR更新的时候,需要将最新的数据推送到搜索引擎中,以方便其他业务系统在搜索引擎中能查询到;
    4. XXX更新的时候,需要更新分布式缓存的XXX数据;
    5. ……

    这些场景都有相似的特征,如下

    特征描述
    异步实际上与主流程关系不大,可以作为附属流程异步执行
    事务大多在事务结束之后执行
    并行可以串行执行,也可以并行执行,对最终结果影响不大

    Mybatis拦截器

    Mybatis提供了一些机制,可以允许我们在做数据库操作的时候进行我们额外的一些程序。当然,这看起来并没有JPAEntityListener好用。

    但是通过这些机制,我们可以达成我们主要的目的,解耦合。(解耦合的好处显而易见,我们在关注主流程业务的时候,附属流程的业务也更容易扩展)

    下面是我们即将用到的一些简单概念,如下

    概念描述
    解耦解耦的本质就是将类之间的直接关系转换成间接关系
    观察者模式一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。
    EventBus基于事件驱动,观察者们监听自己感兴趣的特定事件,进行相应的处理。

    锚定事件发生

    首先,我们要找到事件发生的地方,通常来说我们更愿意在业务代码里写

    1. Company company = new Company();
    2. company.setCompanyName(companyName);
    3. companyMapper.insert(company);
    4. // 创建XXX成功后,触发了一些操作;
    5. doSomething(company);
    6. 复制代码

    但是,这种方式是耦合的,我们在doSomething() 里发生的一些异常会影响到主流程,而每一次增加附属流程的改动,都会产生影响范围的蔓延。

    这里,我们使用Mybatis提供的一种方式来锚定事件发生

    1. @Component
    2. @Intercepts( {
    3. @Signature(method = "update", type = Executor.class, args = {
    4. MappedStatement.class,
    5. Object.class
    6. })
    7. })
    8. public class EntityInterceptor implements Interceptor {
    9. private static final Logger LOGGER = LoggerFactory.getLogger(EntityInterceptor.class);
    10. @Override
    11. public Object intercept(Invocation invocation) throws Throwable {
    12. Object proceed = invocation.proceed();
    13. try {
    14. Object[] args = invocation.getArgs();
    15. MappedStatement ms = (MappedStatement) args[0];
    16. Object params = args[1];
    17. if (SqlCommandType.INSERT.equals(ms.getSqlCommandType())) {
    18. insertCommond(params);
    19. }
    20. if (SqlCommandType.UPDATE.equals(ms.getSqlCommandType())) {
    21. updateCommond(params);
    22. }
    23. } catch (Exception e) {
    24. LOGGER.warn("entity change warning: {}", e.getMessage());
    25. } finally {
    26. return proceed;
    27. }
    28. }
    29. private void insertCommond(Object params) {
    30. if (Objects.isNull(params)) {
    31. return;
    32. }
    33. // 插入Company的事件
    34. if (params instanceof Company) {
    35. Company company = (Company) params;
    36. CompanyEvent companyEvent =
    37. new CompanyEvent(CompanyEvent.TOPIC_ADD, WebUtils.getOpenId(), CompanyVo.from(company));
    38. SpringUtil.getApplicationContext().publishEvent(companyEvent);
    39. LOGGER.info("[发布事件:{}] - [事件内容:{}]", CompanyEvent.TOPIC_ADD, JSON.toJSONString(companyEvent));
    40. }
    41. }
    42. }
    43. 复制代码

    在这里,我们监听了MybatisExecutor对象的update方法,来监听对象的新增和修改。

    1. package org.apache.ibatis.executor;
    2. public interface Executor {
    3. int update(MappedStatement ms, Object parameter) throws SQLException;
    4. }
    5. 复制代码

    Companyinsert 事件发生时,我们发布了一条 CompanyEvent 的事件。

    订阅事件

    我们使用发布-订阅模型实现了解耦合,针对刚才发布的 CompanyEvent 事件,我们来写一个事件消费者。

    1. @Component
    2. public class CompanyListener {
    3. /**
    4. * 监听事件 - 公司.
    5. *
    6. * @param companyEvent 事件.
    7. */
    8. @Lazy
    9. @Async
    10. @TransactionalEventListener(
    11. fallbackExecution = true,
    12. phase = TransactionPhase.AFTER_COMPLETION,
    13. classes = CompanyEvent.class)
    14. public void doAsync(CompanyEvent companyEvent) {
    15. LOGGER.info("[Company 订阅:{}] - [开始执行:{}]",
    16. companyEvent.getTopic(), JSON.toJSONString(companyVo));
    17. }
    18. }
    19. 复制代码

    代码中涉及到一些注解,简单介绍下

    注解介绍
    @Component会注册为Spring的一个Bean
    @Lazy该方法会异步执行
    @TransactionalEventListener在事务的不同阶段去触发执行该监听

    我们标注了该事件是在TransactionPhase.AFTER_COMPLETION(事务提交完成)这个事务节点进行事件处理。

    观察者模式

    上面使用了SpringEventListener来实现的事件驱动,除此之外,还可以使用GuavaEventBusVert.xEventBus等方式实现。

    很多工具类提供了关于EventBus的实现,但是使用逻辑和方式上大多大同小异。

    总结

    观察者模式监听模式都有利于解耦合,根据业务诉求合理的进行开发设计,能为以后的扩展打下坚实的基础。

  • 相关阅读:
    Verilog 不同编码风格对综合电路的影响
    Java实现发送邮件
    CSS 伪类选择器 last-child 和 last-of-type 的区别
    【C++编程语言】之 多态的基本概念 ,纯虚函数和抽象类,虚析构和纯虚析构
    Spring Boot 之 web 开发
    全国职业技能大赛云计算--高职组赛题卷②(容器云)
    (Vue+SpringBoot+elementUi+WangEditer)仿论坛项目
    python结合Airtest框架APP自动化
    std::format格式化自定义类型
    11 编译2022年最新的Linux kernel 6.1源码,并用QEMU模拟器运行
  • 原文地址:https://blog.csdn.net/m0_73311735/article/details/127767965