MyByatis 中使用了哪些设计模式分别是怎样体现的?

2 篇文章 0 订阅
2 篇文章 0 订阅
  1. 工厂模式
    在这里插入图片描述
    工厂模式应用的 常见类 SqlSessionFactory MyByatis 中通过SqlSession 来执行SQL命令 获取映射器示列和管理事务 而SqlSessionFactory就是用来创建SqlSession类对象的
    在这里插入图片描述
    比如在SqlSessionFactory 接口中的 openSession() 方法 他调用的是
    DefaultSqlSessionFactory中的openSessionFromDataSource 方法,代码如下
    在这里插入图片描述
    在代码中有方法 Executor executor = configuration.newExecutor(tx, execType); 就是一个标准的工厂模式,它根据传入的type类型不同,返回不同的 executor
    在这里插入图片描述
  2. 建造者模式(Builder)
    在Mybatis中建造者模式最直观的应用是SqlSessionFactoryBuilder对象
    在这里插入图片描述
    获取SqlSessionFactory对象的方法
    在这里插入图片描述

下面展示一些 内联代码片

 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.
      }
    }
  }

build(parser.parse()) 方法源码如下

 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
// 其中parser.parse() 的源码如下


  public Configuration parse() {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            // 这一行代码就是在 进行属性构建
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }


 private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

在这里插入图片描述

  1. 单列模式

只有一个单纯的对象
在这里插入图片描述


public class ErrorContext {
    private static final String LINE_SEPARATOR = System.lineSeparator();
    // 每个线程变量存储的容器 ThreadLocal 
    private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);
    private ErrorContext stored;
    private String resource;
    private String activity;
    private String object;
    private String message;
    private String sql;
    private Throwable cause;

    private ErrorContext() {
    }

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

    public ErrorContext store() {
        ErrorContext newContext = new ErrorContext();
        newContext.stored = this;
        LOCAL.set(newContext);
        return (ErrorContext)LOCAL.get();
    }

    public ErrorContext recall() {
        if (this.stored != null) {
            LOCAL.set(this.stored);
            this.stored = null;
        }

        return (ErrorContext)LOCAL.get();
    }

    public ErrorContext resource(String resource) {
        this.resource = resource;
        return this;
    }

    public ErrorContext activity(String activity) {
        this.activity = activity;
        return this;
    }

    public ErrorContext object(String object) {
        this.object = object;
        return this;
    }

    public ErrorContext message(String message) {
        this.message = message;
        return this;
    }

    public ErrorContext sql(String sql) {
        this.sql = sql;
        return this;
    }

    public ErrorContext cause(Throwable cause) {
        this.cause = cause;
        return this;
    }

    public ErrorContext reset() {
        this.resource = null;
        this.activity = null;
        this.object = null;
        this.message = null;
        this.sql = null;
        this.cause = null;
        LOCAL.remove();
        return this;
    }

    public String toString() {
        StringBuilder description = new StringBuilder();
        if (this.message != null) {
            description.append(LINE_SEPARATOR);
            description.append("### ");
            description.append(this.message);
        }

        if (this.resource != null) {
            description.append(LINE_SEPARATOR);
            description.append("### The error may exist in ");
            description.append(this.resource);
        }

        if (this.object != null) {
            description.append(LINE_SEPARATOR);
            description.append("### The error may involve ");
            description.append(this.object);
        }

        if (this.activity != null) {
            description.append(LINE_SEPARATOR);
            description.append("### The error occurred while ");
            description.append(this.activity);
        }

        if (this.sql != null) {
            description.append(LINE_SEPARATOR);
            description.append("### SQL: ");
            description.append(this.sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim());
        }

        if (this.cause != null) {
            description.append(LINE_SEPARATOR);
            description.append("### Cause: ");
            description.append(this.cause.toString());
        }

        return description.toString();
    }
}

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

  1. 适配器模式
    ●指将一个不兼容的接口转换成另一个可以兼容的接口
    这样就可以使那些不兼容的类可以一起工作
    ●例如,最早之前我们用的耳机都是圆形的,而现在大多数的耳机和电源都
    统一成了方形的typec接口,那之前的圆形耳机就不能使用了
    只能买一个适配器把圆形接口转化成方形的

个转换头就相当于程序中的适配器模式
适配器模式在MyBatis中的典型代表是Log

MyBatis中的日志模块适配了以下多种日志类型:
SLF4J
Apache Commons Logging
Log4j 2
Log4j
JDK logging

Log 类的源码如下

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);
}

然后MyBatis定义了多个适配接口,例如Log4j2实现源码如下:


public class Log4j2Impl implements Log {
    private final Log log;

    public Log4j2Impl(String clazz) {
        Logger logger = LogManager.getLogger(clazz);
        if (logger instanceof AbstractLogger) {
            this.log = new Log4j2AbstractLoggerImpl((AbstractLogger)logger);
        } else {
            this.log = new Log4j2LoggerImpl(logger);
        }

    }

    public boolean isDebugEnabled() {
        return this.log.isDebugEnabled();
    }

    public boolean isTraceEnabled() {
        return this.log.isTraceEnabled();
    }

    public void error(String s, Throwable e) {
        this.log.error(s, e);
    }

    public void error(String s) {
        this.log.error(s);
    }

    public void debug(String s) {
        this.log.debug(s);
    }

    public void trace(String s) {
        this.log.trace(s);
    }

    public void warn(String s) {
        this.log.warn(s);
    }
}

Slf4jImpl 也是类似这样

public class Slf4jImpl implements Log {
    private Log log;

    public Slf4jImpl(String clazz) {
        Logger logger = LoggerFactory.getLogger(clazz);
        if (logger instanceof LocationAwareLogger) {
            try {
                logger.getClass().getMethod("log", Marker.class, String.class, Integer.TYPE, String.class, Object[].class, Throwable.class);
                this.log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger)logger);
                return;
            } catch (NoSuchMethodException | SecurityException var4) {
            }
        }

        this.log = new Slf4jLoggerImpl(logger);
    }

    public boolean isDebugEnabled() {
        return this.log.isDebugEnabled();
    }

    public boolean isTraceEnabled() {
        return this.log.isTraceEnabled();
    }

    public void error(String s, Throwable e) {
        this.log.error(s, e);
    }

    public void error(String s) {
        this.log.error(s);
    }

    public void debug(String s) {
        this.log.debug(s);
    }

    public void trace(String s) {
        this.log.trace(s);
    }

    public void warn(String s) {
        this.log.warn(s);
    }
}

tip: 查看接口被那些类继承 ctrl+h / control + h
在这里插入图片描述

  1. 代理模式
    ●指的是给某一个对象提供一 个代理对象, 并由代理对象控制原对象的调用
    ●代理模式在生活中也比较常见
    比如超市、小卖店都是-一个个“代理”,他们的最上游是一个个生产厂家
    这些代理负责把厂家生产出来的产品卖出去
    ●代理模式在MyBatis中的典型代表是MapperProxyFactory
public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();

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

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

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

	/**
	* newInstance 就是生成一个代理类 来实现具体的功能
	*/
    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);
    }
}
  1. 模板方法模式
    最常用的设计模式之-
    ●指定义一个操作算法的骨架,一些步骤的实现延迟到子类中去实现
    使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤
    ●此模式是基于继承的思想实现代码复用的
    整个过程都是固定的,唯一变的就是泡入茶叶种类的不同
    比如今天喝的是绿茶,明天可能喝的是红茶,那么我们就可以把流程定义为- -个模板
    而把茶叶的种类延伸到子类中去实现,这就是模板方法的实现思路
    模板方法在MyBatis中的典型代表是BaseExecutor

在这里插入图片描述
比如这里的BaseExecutor.doUpdate 方法在SimpleExecutor 中的实现如下

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;

        int var6;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var6 = handler.update(stmt);
        } finally {
        // 每次调用完毕的时候,都会关闭Statement
            this.closeStatement(stmt);
        }

        return var6;
    }

而在 ReuseExecutor 中每次使用完毕后则不会关闭Statement

  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
        Statement stmt = this.prepareStatement(handler, ms.getStatementLog());
        return handler.update(stmt);
    }
  1. 装饰器模式
    ●允许向一个现有的对象添加新的功能,同时又不改变其结构
    这种类型的设计模式属于结构型模式,它是作为现有类的一个包装
    装饰器模式在生活中很常见,比如装修房子,我们在不改变房子结构的同时
    给房子添加了很多的点缀;比如安装了天然气报警器
    增加了热水器等附加的功能都属于装饰器模式

装饰器模式在MyBatis中的典型代表是Cache
●Cache除了有数据存储和缓存的基本功能外(由PerpetualCache永久缓存实现)
还有其他附加的Cache类,比如先进先出的FifoCache、最近最少使用的LruCache
●防止多线程并发访问的SynchronizedCache等众多附加功能的缓存类
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值