Mybatis 使用的 9 种设计模式,真是太有用了(二)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Mybatis 使用的 9 种设计模式,真是太有用了(二)
  • 6、模板方法模式
  • 7、适配器模式
  • 8、装饰者模式
  • 9、迭代器模式

6、模板方法模式

模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。

模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。

模板类定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

image.png

在Mybatis中,sqlSession的SQL执行,都是委托给Executor实现的,Executor包含以下结构:

image.png

其中的BaseExecutor就采用了模板方法模式,它实现了大部分的SQL执行逻辑,然后把以下几个方法交给子类定制化完成:

protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
 protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;
 protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
   ResultHandler resultHandler, BoundSql boundSql) throws SQLException;

该模板方法类有几个子类的具体实现,使用了不同的策略:

  • 简单SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)
  • 重用ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。(可以是Statement或PrepareStatement对象)
  • 批量BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;BatchExecutor相当于维护了多个桶,每个桶里都装了很多属于自己的SQL,就像苹果蓝里装了很多苹果,番茄蓝里装了很多番茄,最后,再统一倒进仓库。(可以是Statement或PrepareStatement对象)

比如在SimpleExecutor中这样实现update方法:

@Override
 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
   Configuration configuration = ms.getConfiguration();
   StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null,
     null);
   stmt = prepareStatement(handler, ms.getStatementLog());
   return handler.update(stmt);
  } finally {
   closeStatement(stmt);
  }
 }

7、适配器模式

适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

image.png

在Mybatsi的logging包中,有一个Log接口:

/**
 * @author Clinton Begin
 */
public interface Log {
 boolean isDebugEnabled();
 boolean isTraceEnabled();
 void error(String s, Throwable e);
 void error(String s);
 void debug(String s);
 void trace(String s);
 void warn(String s);
}

该接口定义了Mybatis直接使用的日志方法,而Log接口具体由谁来实现呢?Mybatis提供了多种日志框架的实现,这些实现都匹配这个Log接口所定义的接口方法,最终实现了所有外部日志框架到Mybatis日志包的适配:

image.png

比如对于Log4jImpl的实现来说,该实现持有了org.apache.log4j.Logger的实例,然后所有的日志方法,均委托该实例来实现。

public class Log4jImpl implements Log {
 private static final String FQCN = Log4jImpl.class.getName();
 private Logger log;
 public Log4jImpl(String clazz) {
  log = Logger.getLogger(clazz);
 }
 @Override
 public boolean isDebugEnabled() {
  return log.isDebugEnabled();
 }
 @Override
 public boolean isTraceEnabled() {
  return log.isTraceEnabled();
 }
 @Override
 public void error(String s, Throwable e) {
  log.log(FQCN, Level.ERROR, s, e);
 }
 @Override
 public void error(String s) {
  log.log(FQCN, Level.ERROR, s, null);
 }
 @Override
 public void debug(String s) {
  log.log(FQCN, Level.DEBUG, s, null);
 }
 @Override
 public void trace(String s) {
  log.log(FQCN, Level.TRACE, s, null);
 }
 @Override
 public void warn(String s) {
  log.log(FQCN, Level.WARN, s, null);
 }
}

8、装饰者模式

装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。

image.png

在mybatis中,缓存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定义。整个体系采用装饰器设计模式,数据存储和缓存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久缓存实现,然后通过一系列的装饰器来对PerpetualCache永久缓存进行缓存策略等方便的控制。如下图:

image.png

用于装饰PerpetualCache的标准装饰器共有8个(全部在org.apache.ibatis.cache.decorators包中):

  1. FifoCache:先进先出算法,缓存回收策略
  2. LoggingCache:输出缓存命中的日志信息
  3. LruCache:最近最少使用算法,缓存回收策略
  4. ScheduledCache:调度缓存,负责定时清空缓存
  5. SerializedCache:缓存序列化和反序列化存储
  6. SoftCache:基于软引用实现的缓存管理策略
  7. SynchronizedCache:同步的缓存装饰器,用于防止多线程并发访问
  8. WeakCache:基于弱引用实现的缓存管理策略

另外,还有一个特殊的装饰器TransactionalCache:事务性的缓存

正如大多数持久层框架一样,mybatis缓存同样分为一级缓存和二级缓存

  • 一级缓存,又叫本地缓存,是PerpetualCache类型的永久缓存,保存在执行器中(BaseExecutor),而执行器又在SqlSession(DefaultSqlSession)中,所以一级缓存的生命周期与SqlSession是相同的。
  • 二级缓存,又叫自定义缓存,实现了Cache接口的类都可以作为二级缓存,所以可配置如encache等的第三方缓存。二级缓存以namespace名称空间为其唯一标识,被保存在Configuration核心配置对象中。

二级缓存对象的默认类型为PerpetualCache,如果配置的缓存是默认类型,则mybatis会根据配置自动追加一系列装饰器。

Cache对象之间的引用顺序为:

SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache

9、迭代器模式

迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。

image.png

Java的Iterator就是迭代器模式的接口,只要实现了该接口,就相当于应用了迭代器模式:

image.png

比如Mybatis的PropertyTokenizer是property包中的重量级类,该类会被reflection包中其他的类频繁的引用到。这个类实现了Iterator接口,在使用时经常被用到的是Iterator接口中的hasNext这个函数。

public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
 private String name;
 private String indexedName;
 private String index;
 private String children;
 public PropertyTokenizer(String fullname) {
  int delim = fullname.indexOf('.');
  if (delim > -1) {
   name = fullname.substring(0, delim);
   children = fullname.substring(delim + 1);
  } else {
   name = fullname;
   children = null;
  }
  indexedName = name;
  delim = name.indexOf('[');
  if (delim > -1) {
   index = name.substring(delim + 1, name.length() - 1);
   name = name.substring(0, delim);
  }
 }
 public String getName() {
  return name;
 }
 public String getIndex() {
  return index;
 }
 public String getIndexedName() {
  return indexedName;
 }
 public String getChildren() {
  return children;
 }
 @Override
 public boolean hasNext() {
  return children != null;
 }
 @Override
 public PropertyTokenizer next() {
  return new PropertyTokenizer(children);
 }
 @Override
 public void remove() {
  throw new UnsupportedOperationException(
    "Remove is not supported, as it has no meaning in the context of properties.");
 }
}

可以看到,这个类传入一个字符串到构造函数,然后提供了iterator方法对解析后的子串进行遍历,是一个很常用的方法类。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
6月前
|
设计模式 Java 数据库连接
MyBatis 设计模式解析
MyBatis 设计模式解析
|
设计模式 SQL XML
「分享」从Mybatis源码中,学习到的10种设计模式
23种设计模式,Mybatis源码中竟然用了一半!怒卷Mybatis设计模式,从源码中学习设计模式,太爽了!
2457 2
「分享」从Mybatis源码中,学习到的10种设计模式
|
设计模式 Java 数据库连接
Mybatis源码分析一-Mybatis基础架构以及设计模式
Mybatis源码分析一-Mybatis基础架构以及设计模式
172 0
|
设计模式 SQL 存储
【MyBatis】面试官:MyBatis中是如何使用设计模式的?
本篇文章了解一下MyBatis中运用到的设计模式,我们在实际的开发中很大程度上只是对设计模式留在一个理论的环节上,缺少实践,通过源码,我们可以学习一下这些设计模式的实践方式,有利于我们能够更深入的理解和使用设计模式。
|
SQL 设计模式 JavaScript
Mybatis 使用的 9 种设计模式,真是太有用了(一)
Mybatis 使用的 9 种设计模式,真是太有用了
Mybatis 使用的 9 种设计模式,真是太有用了(一)
|
设计模式 SQL XML
MyBatis 中的设计模式
前言 软件开发的流程一般可分为分析、设计、实现,设计模式在处于设计或代码实现阶段,以设计思想、设计原则作为指导,相对来说更为具象,是前人对经常遇到的设计问题总结出的一套解决方案,多数设计模式用来解决代码的扩展性问题,在框架中使用的场景较多。
306 0
|
设计模式 安全 Java
设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解
设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解
设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解
|
1月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
106 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
54 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
1月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
295 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个