【簡寫MyBatis】01-簡單映射器

前言

新開一個坑,爲了學習一下MyBatis的源碼,寫代碼是次要的,主要爲了吸收一下其中的思想和手法。

目的

關聯對象接口和映射類的問題,把 DAO 接口使用代理類,包裝映射操作。

知識點

  1. 動態代理
  2. 簡單工廠模式
  3. InvocationHandler接口的使用

實現

既然是簡易的MyBatis編寫,那肯定得看下源碼了;先來一波回憶,MyBatis的使用:

忘記的朋友可以看下之前寫的MyBatis手冊: https://blog.csdn.net/weixin_43908900/article/details/129780085

https://www.cnblogs.com/xbhog/p/17258782.html

@Test
public void testMybatis(){
    //加載核心配置文件
    try {
        //字符流加載配置
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        //創建sql連接工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //創建session連接,設置true,默認提交事務
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        //反射獲取類對象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userAll = mapper.getUserAll();
        System.out.println(userAll);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

代碼中可以看到,接口和映射器有關係的地方應該是sqlSession.getMapper(UserMapper.class);點進去。

先看映射器工廠類: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 mapperInterface;
  }

  public Map<Method, MapperMethodInvoker> 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<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

簡易映射器類圖:

MapperProxy代理類來代理需要使用的接口,爲了方便後續的維護擴展,在代理類上加一層代理工廠類MapperProxyFactory

使用代理類的好處是: 動態代理允許 MyBatis 在不修改接口實現的情況下,爲接口方法提供自定義的行爲。這意味着開發者只需要定義接口和 SQL 映射,而無需編寫接口的實現類。這種設計促進了關注點分離,使得數據訪問邏輯(SQL)與業務邏輯更加清晰,MapperProxy 能夠在運行時將 SQL 語句與接口方法動態綁定,這樣,MyBatis 可以根據接口方法的簽名和註解或 XML 配置來執行相應的 SQL 操作。

使用簡單工廠模式的好處: 實現代碼複用和模塊化,對代理邏輯和接口使用解耦,靈活性高,不改變公共接口等。

總之都是爲了項目的高度靈活、擴展、複用等。

通過上述的分析,現在進行代碼編寫的流程比較明朗了。

代理類的實現:

public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;
    //模擬SqlSession
    private Map<String, String> sqlSession;
    private final Class<T> mapperInterface;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //檢查一個方法是否來自特定的類或者是一系列接口中的一個
        //是Object自身的方法,就沒必要代理,直接調用就行
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            //需要匹配的是類名+方法名
            return "你的被代理了!" + sqlSession.get(mapperInterface.getName() + "." + method.getName());
        }
    }

}

映射器代理工廠實現:

public class MapperProxyFactory<T> {

    private final Class<T> mapperInterface;

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

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

}

執行流程如下:

測試

public void testApp() {
    MapperProxyFactory<IUserDao> proxyFactory = new MapperProxyFactory<>(IUserDao.class);
    Map<String,String> sqlSession = new HashMap<>();
    sqlSession.put("com.xbhog.IUserDao.getUserName","模擬執行 Mapper.xml 中 SQL 語句的操作:查詢用戶姓名");
    IUserDao userDao = proxyFactory.newInstance(sqlSession);
    String userName = userDao.getUserName("100001");
    System.out.println(userName);
}

總結

  1. 通過追溯MyBatis中的源碼,明確本文的主要的內容
  2. 明確目標類的依賴關係
  3. 代碼實現簡易效果
  4. 明確執行流程
  5. 測試代碼,符合預期結果

參考&學習

https://mp.weixin.qq.com/s/G3fZES2FvNQK8JLnd9Hx9w

AI大模型輔助

MyBatis源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章