Mybatis 之 设计模式

Mybatis至少用到了以下的设计模式的使用:

模式mybatis 体现
Builder模式例如SqlSessionFactoryBuilder、Environment;
工厂方法模式例如SqlSessionFactory、TransactionFactory、LogFactory
单例模式例如 ErrorContext 和 LogFactory
代理模式Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理还有executor.loader包使用了 cglib或者javassist达到延迟加载的效果
组合模式例如SqlNode和各个子类ChooseSqlNode等
模板方法模式例如 BaseExecutor 和 SimpleExecutor,还有 BaseTypeHandler 和所有的⼦类例如IntegerTypeHandler
适配器模式例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现
装饰者模式例如Cache包中的cache.decorators子包中等各个装饰者的实现
迭代器模式例如迭代器模式PropertyTokenizer

        接下来对Builder构建者模式、工厂模式、代理模式进行解读,先介绍模式自身的知识,然后解读在Mybatis中怎样应用了该模式。

1、Builder构建者模式

        Builder模式的定义是"将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。”,它属于创建类模式,一般来说,如果一个对象的构建比较复杂,超出了构造函数所能包含的范围,就可以使用工厂模式和Builder模式,相对于工厂模式会产出一个完整的产品,Builder应用于更加复杂的对象的构建,甚⾄只会构建产品的一个部分,直白来说,就是使用多个简单的对象一步一步构建成⼀个复杂的对象 

Mybatis中的体现

SqlSessionFactory 的构建过程:

        Mybatis的初始化工作非常复杂,不是只用一个构造函数就能搞定的。所以使用了建造者模式,使用了大量的Builder,进行分层构造,核心对象Configuration使用了 XmlConfigBuilder来进行构造 。

        在Mybatis环境的初始化过程中,SqlSessionFactoryBuilder会调用XMLConfigBuilder读取所有的MybatisMapConfig.xml 和所有的 *Mapper.xml 文件,构建 Mybatis 运行的核心对象 Configuration对象,然后将该Configuration对象作为参数构建一个SqlSessionFactory对象。 

    private void parseConfiguration(XNode root) {
        try {
//issue #117 read properties first
//解析<properties />标签
            propertiesElement(root.evalNode("properties"));
// 解析 <settings /> 标签
            Properties settings = settingsAsProperties(root.evalNode("settings"));
//加载⾃定义的VFS实现类
            loadCustomVfs(settings);
// 解析 <typeAliases /> 标签
            typeAliasesElement(root.evalNode("typeAliases"));
//解析<plugins />标签
            pluginElement(root.evalNode("plugins"));
// 解析 <objectFactory /> 标签
            objectFactoryElement(root.evaINode("obj ectFactory"));
// 解析 <objectWrapper Factory /> 标签
            objectFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析 <reflectorFactory /> 标签
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 赋值 <settings /> 到 Configuration 属性
            settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 解析 <environments /> 标签
            environmentsElement(root.evalNode("environments"));
// 解析 <databaseIdProvider /> 标签
            databaseldProviderElement(root.evalNode("databaseldProvider"));
        }

        其中 XMLConfigBuilder 在构建 Configuration 对象时,也会调用 XMLMapperBuilder 用于读取*Mapper 文件,而XMLMapperBuilder会使用XMLStatementBuilder来读取和build所有的SQL语句。

//解析<mappers />标签
mapperElement(root.evalNode("mappers")); 

        在这个过程中,有一个相似的特点,就是这些Builder会读取文件或者配置,然后做大量的XpathParser解析、配置或语法的解析、反射生成对象、存入结果缓存等步骤,这么多的工作都不是⼀个构造函数所能包括的,因此大量采用了 Builder模式来解决 。

SqlSessionFactoryBuilder类根据不同的输入参数来构建SqlSessionFactory这个工厂对象 

2、工厂模式 

在Mybatis中比如SqlSessionFactory使用的是工厂模式,该工厂没有那么复杂的逻辑,是一个简单工厂模式。

        简单工厂模式(Simple Factory Pattern):有称为静态工厂方法(Static Factory Method)模式,它属于创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类

Mybatis 体现:
        Mybatis中执行Sql语句、获取Mappers、管理事务的核心接口SqlSession的创建过程使用到了工厂模式。

有⼀个 SqlSessionFactory 来负责 SqlSession 的创建 

SqlSessionFactory:

        可以看到,该Factory的openSession ()方法重载了很多个,分别支持autoCommitExecutorTransaction等参数的输入,来构建核心的SqlSession对象。

在DefaultSqlSessionFactory的默认工厂实现里,有一个方法可以看出工厂怎么产出一个产品: 

    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);
            //根据参数创建制定类型的Executor
            final Executor executor = configuration.newExecutor(tx, execType);
//返回的是 DefaultSqlSession
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            closeTransaction(tx); // may have fetched a connection so lets call close()
            throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }

        这是⼀个openSession调用的底层方法,该方法先从configuration读取对应的环境配置,然后初始化TransactionFactory 获得一个 Transaction 对象,然后通过 Transaction 获取一个 Executor 对象,最后通过configuration、Executor、是否autoCommit三个参数构建了 SqlSession。

3、代理模式

        代理模式(Proxy Pattern):给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy,它是一种对象结构型模式,代理模式分为静态代理动态代理,我们来介绍动态代理。

Mybatis中实现:

        代理模式可以认为是Mybatis的核心使用的模式,正是由于这个模式,我们只需要编写Mapper.java接口,不需要实现,由Mybatis后台帮我们完成具体SQL的执行。

        当我们使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,而该方法又会调用 mapperProxyFactory.newInstance(sqlSession)来生成一个具体的代理:

    public class MapperProxyFactory<T> {
        private final Class<T> mapperInterface;
        private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
        public MapperProxyFactory(Class<T> mapperInterface) {
            this.mapperInterface = mapperInterface;
        }
        public Class<T> getMapperInterface() {
            return mapperInterface;
        }
        public Map<Method, MapperMethod> getMethodCache() {
            return methodCache;
            @SuppressWarnings("unchecked")
            protected T newInstance(MapperProxy<T> mapperProxy) {
                return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
            }
            public T newInstance(SqlSession sqlSession) {
                final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
                return newInstance(mapperProxy);
            }
        }

        在这里,先通过T newInstance(SqlSession sqlSession)方法会得到⼀个MapperProxy对象,然后调用T newInstance(MapperProxy mapperProxy)生成代理对象然后返回。而查看MapperProxy的代码,可以看到如下内容:

    public class MapperProxy<T> implements InvocationHandler, Serializable {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                if (Object.class.equals(method.getDeclaringClass())) {
                    return method.invoke(this, args);
                } else if (isDefaultMethod(method)) {
                    return invokeDefaultMethod(proxy, method, args);
                }
            } catch (Throwable t) {
                throw ExceptionUtil.unwrapThrowable(t);
            }
            final MapperMethod mapperMethod = cachedMapperMethod(method);
            return mapperMethod.execute(sqlSession, args);
        }

        非常典型的,该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法。通过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper接口的时候,就会转发给MapperProxy.invoke方法,而该方法则会调用后续的sqlSession.cud>executor.execute>prepareStatement 等一系列方法,完成 SQL 的执行和返回

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悠然予夏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值