手写几万行的Mybatis,把原理搞的透透的

mybatis 10 种设计模式

工厂模式 -> factory,建造者 -> builder

代理模式,创建复杂对象(非硬编码)所必须的,分静态和动态,一般用动态

装饰器,对原有的进行增强,比如一级缓存被包装,产生二级缓存

要定义标准流程,离不开模板模式,模板模式用抽象方法来定义一些标准流程

解决同一类问题时,因为一类下的问题也会有不同差异,所以使用策略模式

创建型模式

工厂模式

工厂模式负责延迟创建

sqlsession 的所有增删改查操作都是由执行器完成的,执行器只有 update 和 select 方法





单例模式

一般都会自己提供创建对象的过程,而不是依赖于其他框架(如 spring)




建造者模式

所有对象创建和 xml 解析基本上都是建造者模式创建的吗,一步一步构建复杂对象,而不是全部放到某个方法



结构型模式

适配器模式





代理模式

代理模式是很多框架的基石

代理模式在 mybatis 中将 configure 配置信息和 xml mapper 关联起来,并通过代理对象调用执行方法等



组合模式

将 sql 节点的解析过程串联起来,如果有新节点可以方便地扩展






装饰器模式



行为型模式

模板模式

常见,通常提供一个抽象类、base 基础类等定义一整套抽象方法,交给子类实现



策略模式

减少 if 判断






迭代器模式

将一个对象,有多少元素,然后继续拆解看子元素还有没有子元素,就是一个迭代的过程



创建简单的映射器代理工厂



package org.malred;

import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;
import org.junit.Test;
import org.malred.dao.IUserDao;
import org.malred.mybatis.binding.MapperProxyFactory;

import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class ApiTest {

    @Test
    public void test_MapperProxyFactory() {
        MapperProxyFactory<IUserDao> factory = new MapperProxyFactory<>(IUserDao.class);

        Map<String, String> sqlSession = new HashMap<>();
        sqlSession.put("org.malred.dao.IUserDao.queryUserName",
                "模拟执行 Mapper.xml 中的SQL语句,操作:查询用户名称");

        IUserDao userDao = factory.newInstance(sqlSession);
        String res = userDao.queryUserName("1");
        System.out.println(res);
    }

    @Test
    public void test_proxy_class() {
        // jdk提供的代理
        IUserDao userDao = (IUserDao) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{IUserDao.class},
                ((proxy, method, args) -> "你被代理了")
        );
        String res = userDao.queryUserName("1");
        System.out.println(res);
    }
}
package org.malred.dao;

public interface IUserDao {
    String queryUserName(String uid);

    Integer queryUserAge(String uid);
}
package org.malred.mybatis.binding;

import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * 映射器代理工厂
 */
public class MapperProxyFactory<T> {
    // 被代理的接口
    private final Class<T> mapperInterface;

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

    public T newInstance(Map<String, String> sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy<>(mapperInterface, sqlSession);
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
                new Class[]{mapperInterface}, mapperProxy);
    }
}
package org.malred.mybatis.binding;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * 映射器代理类
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final Long serialVersionUID = -9646882346141251L;
    private final Class<T> mapperInterface;
    // 模拟存放的参数
    private Map<String, String> sqlSession;

    public MapperProxy(Class<T> mapperInterface, Map<String, String> sqlSession) {
        this.mapperInterface = mapperInterface;
        this.sqlSession = sqlSession;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // object提供的equals方法不应该被代理
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            return "你被代理了! " + sqlSession.get(mapperInterface.getName() + "." + method.getName());
        }
    }
}

实现映射器的注册和使用


  目录