跳至主要內容

MyBatis-设计模式

holic-x...大约 8 分钟JAVA框架

MyBatis-设计模式

学习核心

  • MyBatis的设计模式

学习资料

MyBatis中的设计模式

设计模式(MyBatis3.5.5版本

【1】SqlSessionFactory:工厂模式

【2】SqlSessionFactoryBuilder:建造者模式

【3】ErrorContext:单例模式

【4】日志模块 Log :适配器模式

【5】MapperProxyFactory:代理模式

【6】Executor:模板方法模式

【7】缓存模块 Cache :装饰器模式

1.SqlSessionFactory:工厂模式

​ SqlSession 是 MyBatis 中的重要 Java 接口,可以通过该接口来执行 SQL 命令、获取映射器示例和管理事务,SqlSessionFactory则是用于生产SqlSession 对象。

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

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    public SqlSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }
    
    ......
}

openSessionFromDataSource

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;

  DefaultSqlSession var8;
  try {
    Environment environment = this.configuration.getEnvironment();
    TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    // 读取对应的环境配置(newExecutor方法为一个标准的工厂模式)
    Executor executor = this.configuration.newExecutor(tx, execType);
    var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
  } catch (Exception var12) {
    Exception e = var12;
    this.closeTransaction(tx);
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }

  return var8;
}

newExecutor:一个标准的工厂模式,会根据ExecutorType传递的值生成相应的对象然后返回

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? this.defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Object 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 (this.cacheEnabled) {
    executor = new CachingExecutor((Executor)executor);
  }

  Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
  return executor;
}

2.SqlSessionFactoryBuilder:建造者模式

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

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

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

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

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

跟踪SqlSessionFactoryBuilder源码(build方法)

// 关注build方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  SqlSessionFactory var5;
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    var5 = this.build(parser.parse());
  } catch (Exception var14) {
    Exception e = var14;
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();

    try {
      inputStream.close();
    } catch (IOException var13) {
    }

  }

  return var5;
}


public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

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

3.ErrorContext:单例模式

​ 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,此模式保证某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类。单例模式场景:比如一个人一生当中只能有一个真实的身份证号,每个收费站的窗口都只能一辆车子一辆车子的经过,类似的场景都是属于单例模式。

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

跟踪ErrorContext源码(摘选其单例的核心实现)

​ ErrorContext单例模式的构建思路:

  • 构造函数私有化
  • 定义一个静态的自身类型LOCAL对象
  • 对外提供instance方法供其他访问调用
public class ErrorContext {
    private static final String LINE_SEPARATOR = System.lineSeparator();
    private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);
    
    private ErrorContext() {
    }

    public static ErrorContext instance() {
        return (ErrorContext)LOCAL.get();
    }
    
    .......    
}

4.日志模块Log:适配器模式

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

​ 例如,最早之前我们用的耳机都是圆形的,而现在大多数的耳机和电源都统一成了方形的 typec 接口,那之前的圆形耳机就不能使用了,只能买一个适配器把圆形接口转化成方形的。而这个转换头就相当于程序中的适配器模式。

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

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

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

package org.apache.ibatis.logging;

public interface Log {
    boolean isDebugEnabled();

    boolean isTraceEnabled();

    void error(String var1, Throwable var2);

    void error(String var1);

    void debug(String var1);

    void trace(String var1);

    void warn(String var1);
}

并提供了多个适配器接口实现

image-20240614180500891

5.MapperProxyFactory:代理模式

​ 代理接口模式(mapper接口和mapper.xml关联)

​ 代理模式指的是给某一个对象提供一个代理对象,并由代理对象控制原对象的调用。

​ 代理模式在生活中也比较常见,比如我们常见的超市、小卖店其实都是一个个“代理”,他们的最上游是一个个生产厂家,他们这些代理负责把厂家生产出来的产品卖出去。

代理模式在 MyBatis 中的典型代表是 MapperProxyFactory

MapperProxyFactory 的 newInstance() 方法就是生成一个具体的代理来实现功能的

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperProxy.MapperMethodInvoker> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperProxy.MapperMethodInvoker> getMethodCache() {
        return this.methodCache;
    }

  	// 创建代理类
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}

6.Executor:模板方法模式

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

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

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

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

在 MyBatis 中 BaseExecutor 实现了大部分 SQL 执行的逻辑,然后再把几个方法交给子类来实现,它的继承关系如下图所示

image-20240614180935352

​ Executor定义一套模板步骤,由子类去决定指定步骤的方法细节

public interface Executor {
    ResultHandler NO_RESULT_HANDLER = null;

    int update(MappedStatement var1, Object var2) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;

    <E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;

    List<BatchResult> flushStatements() throws SQLException;

    void commit(boolean var1) throws SQLException;

    void rollback(boolean var1) throws SQLException;

    CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);

    boolean isCached(MappedStatement var1, CacheKey var2);

    void clearLocalCache();

    void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);

    Transaction getTransaction();

    void close(boolean var1);

    boolean isClosed();

    void setExecutorWrapper(Executor var1);
}

​ 由子类继承Executor,然后重写父类方法的实现

7.Cache:装饰器模式

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

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

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

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

image-20240614181451047

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3