MyBatis 使用的设计模式

MyBatis 使用了哪些设计模式?在源码中是如何体现的?

1. Mybatis的使用流程

// 1. 读取配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// 2. 创建 SqlSessionFactory 工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
// 3. 从工厂获取 SqlSession 对象
SqlSession sqlSession = factory.openSession();
// 4. 使用 SqlSession 创建 dao接口的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 5. 使用代理对象执行查询
List<User> users = mapper.listAllUsers();
users.forEach(System.out::println);
// 6. 释放资源
sqlSession.close();
is.close();

首先使用SqlSessionFactoryBuilder 建造出SqlSessionFactory,这里使用了建造者模式
然后SqlSessionFactory工厂创建出SqlSession ,通过SqlSession执行 SQL 命令、获取映射器示例和管理事务,这里创建SqlSession 使用了工厂模式

初次之外还有单例模式,模板方法模式等等,下面一一介绍

2. 建造者模式(Builder

建造者模式指的是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。也就是说建造者模式是通过多个模块一步步实现了对象的构建,相同的构建过程可以创建不同的产品

例如,组装电脑,最终的产品就是一台主机,然而不同的人对它的要求是不同的,比如设计人员需要显卡配置高的;而影片爱好者则需要硬盘足够大的(能把视频都保存起来),但对于显卡却没有太大的要求,我们的装机人员根据每个人不同的要求,组装相应电脑的过程就是建造者模式

建造者模式在 MyBatis 中的典型代表是 SqlSessionFactoryBuilder

普通的对象都是通过 new 关键字直接创建的,但是如果创建对象需要的构造参数很多,且不能保证每个参数都是正确的或者不能一次性得到构建所需的所有参数,那么就需要将构建逻辑从对象本身抽离出来,让对象只关注功能,把构建交给构建类,这样可以简化对象的构建,也可以达到分步构建对象的目的,而 SqlSessionFactoryBuilder 的构建过程正是如此

SqlSessionFactoryBuilder 中构建 SqlSessionFactory 对象的过程是这样的,首先需要通过 XMLConfigBuilder 对象读取并解析 XML 的配置文件,然后再将读取到的配置信息存入到 Configuration 类中,然后再通过 build 方法生成我们需要的 DefaultSqlSessionFactory 对象,实现源码如下(在 SqlSessionFactoryBuilder 类中)

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        return build(parser.parse());
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
        inputStream.close();
        } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
        }
    }
}
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

SqlSessionFactoryBuilder 类相当于一个建造工厂,先读取文件或者配置信息、再解析配置、然后通过反射生成对象,最后再把结果存入缓存,这样就一步步构建造出一个 SqlSessionFactory 对象

3. 工厂模式

工厂模式就是提供一个工厂类,当有客户端需要调用的时候,只调用这个工厂类就可以得到自己想要的结果,从而无需关注某类的具体实现过程。这就好比你去餐馆吃饭,可以直接点菜,而不用考虑厨师是怎么做的。

工厂模式在 MyBatis 中的典型代表是 SqlSessionFactory

SqlSessionMyBatis 中的重要 Java 接口,可以通过该接口来执行 SQL 命令、获取映射器示例和管理事务,而 SqlSessionFactory 正是用来产生 SqlSession 对象的,所以它在 MyBatis 中是比较核心的接口之一。

工厂模式应用解析:SqlSessionFactory 是一个接口类,它的子类 DefaultSqlSessionFactorys 有一个 openSession(ExecutorType execType) 的方法,其中使用了工厂模式,源码如下:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        final Executor executor = configuration.newExecutor(tx, execType);
        return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
        closeTransaction(tx); 
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

从该方法我们可以看出它会 configuration.newExecutor(tx, execType) 读取对应的环境配置,而此方法的源码如下:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

可以看出 newExecutor() 方法为标准的工厂模式,它会根据传递 ExecutorType 值生成相应的对象然后进行返回

4. 单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,此模式保证某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类

单例模式在 MyBatis 中的典型代表是 ErrorContext

ErrorContext 是线程级别的的单例,每个线程中有一个此对象的单例,用于记录该线程的执行环境的错误信息。

ErrorContext 的实现源码如下:

public class ErrorContext {
  private static final String LINE_SEPARATOR = System.lineSeparator();
  // 每个线程存储的容器
  private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);
  public static ErrorContext instance() {
    return LOCAL.get();
  }
  // 忽略其他
}

可以看出 ErrorContext 使用 private 修饰的 ThreadLocal 来保证每个线程拥有一个 ErrorContext 对象,在调用 instance() 方法时再从 ThreadLocal 中获取此单例对象

5. 适配器模式

适配器模式是指将一个不兼容的接口转换成另一个可以兼容的接口,这样就可以使那些不兼容的类可以一起工作

适配器模式在 MyBatis 中的典型代表是 LogMyBatis 中的日志模块适配了以下多种日志类型:

SLF4J
Apache Commons Logging
Log4j 2
Log4j
JDK logging

首先 MyBatis 定义了一个 Log 的接口,用于统一和规范接口的行为,源码如下:

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 定义了多个适配接口,例如 Log4j2 实现源码如下:

public class Log4j2Impl implements Log {
  private final Log log;
  public Log4j2Impl(String clazz) {
    Logger logger = LogManager.getLogger(clazz);
    if (logger instanceof AbstractLogger) {
      log = new Log4j2AbstractLoggerImpl((AbstractLogger) logger);
    } else {
      log = new Log4j2LoggerImpl(logger);
    }
  }
  @Override
  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }
  @Override
  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }
  @Override
  public void error(String s, Throwable e) {
    log.error(s, e);
  }
  @Override
  public void error(String s) {
    log.error(s);
  }
  @Override
  public void debug(String s) {
    log.debug(s);
  }
  @Override
  public void trace(String s) {
    log.trace(s);
  }
  @Override
  public void warn(String s) {
    log.warn(s);
  }
}

这样当你项目中添加了 Log4j2 时,MyBatis 就可以直接使用它打印 MyBatis 的日志信息了。Log 的所有子类如下图所示:
在这里插入图片描述

6. 模板方法模式

模板方法模式是最常用的设计模式之一,它是指定义一个操作算法的骨架,而将一些步骤的实现延迟到子类中去实现,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。此模式是基于继承的思想实现代码复用的

例如,我们喝茶的一般步骤都是这样的:

把热水烧开
把茶叶放入壶中
等待一分钟左右
把茶倒入杯子中
喝茶

整个过程都是固定的,唯一变的就是泡入茶叶种类的不同,比如今天喝的是绿茶,明天可能喝的是红茶,那么我们就可以把流程定义为一个模板,而把茶叶的种类延伸到子类中去实现,这就是模板方法的实现思路。

模板方法在 MyBatis 中的典型代表是 BaseExecutor

MyBatisBaseExecutor 实现了大部分 SQL 执行的逻辑,然后再把几个方法交给子类来实现,它的继承关系如下图所示:
在这里插入图片描述

7. 装饰器模式

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构,这种类型的设计模式属于结构型模式,它是作为现有类的一个包装。

装饰器模式在生活中很常见,比如装修房子,我们在不改变房子结构的同时,给房子添加了很多的点缀;比如安装了天然气报警器,增加了热水器等附加的功能都属于装饰器模式。

装饰器模式在 MyBatis 中的典型代表是 Cache

Cache 除了有数据存储和缓存的基本功能外(由 PerpetualCache 永久缓存实现),还有其他附加的 Cache 类,比如先进先出的 FifoCache、最近最少使用的 LruCache、防止多线程并发访问的 SynchronizedCache 等众多附加功能的缓存类,Cache 所有实现子类如下图所示
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值