源码剖析 Mybatis 映射器(Mapper)工作原理

本文深入剖析Mybatis映射器接口的工作原理,从基础回顾、SQL与Mapper接口的绑定关系、动态代理类的生成以及MapperMethod的拦截逻辑四个方面进行源码分析,揭示了Mybatis如何通过JDK动态代理根据接口和方法执行对应的SQL,并通过SqlSession进行数据操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击上方"田守枝的技术博客",关注我

       

Mybatis可以说是目前国内使用最广泛的ORM框架。最原始的使用方式下,我们将sql写在xml配置文件中,通过SqlSession,根据statementId来唯一指定要执行的sql。从Mybatis 3.0之后,我们可以通过一个Mapper映射接口来完成相同的功能。你是否思考过,Mapper映射接口内部是如何完成这样的功能的。本文从源码的角度,深入分析mybatis 映射器接口的工作原理。

1 基础回顾

    在最原始的情况下,我们需要使用SqlSession类,通过namespace.id方式来定位一个sql,如:

String namespace="com.tianshouzhi.mybatis.UserMapper";
sqlSession.insert(namespace+".insert",user);
sqlSession.selectOne(namespace+".selectById",1);
sqlSession.update(namespace+".update",user);
sqlSession.delete(namespace+".deleteById",1);

    从Mybatis 3.0开始,引入了Mapper映射器接口,我们可以直接通过一个接口来引用需要使用的sql。只要这个接口满足以下条件,即可以引用xml配置文件中的sql:

  • 接口的全路径就是映射文件的namespace属性值

  • 接口中定义的方法,方法名与映射文件中<insert>、<select>、<delete>、<update>等元素的id属性值相同

例如,定义一个UserMapper接口

package com.tianshouzhi.mybatis;
public interface UserMapper {
    public int insert(User user);
    public User selectById(int id);
    public int updateById(User user);
    public int deleteById(int id);
}

之后我们就可以直接使用UserMapper类来进行增删改查,如下:

User user=...
SqlSession  sqlSession = sqlSessionFactory.openSession();
try{
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    int insertCount = userMapper.insert(user);
    user=userMapper.selectById(1);
    userMapper.updateById(user);
    userMapper.deleteById(1);
} finally {
    sqlSession.close();
}

        这里的原理很简单:

当接口方法执行时,首先通过反射拿到当前接口的全路径当做namespace,然后把执行的方法名当成id,拼接成namespace.id,最后在xml映射文件中寻找对应的sql。 

在匹配上某个sql之后,底层实际上还是利用SqlSession的相关方法来进行操作,只不过这个过程对于用户来说屏蔽了。

另外,mybatis还会自动的根据Mapper接口方法的返回值类型,选择调用SqlSession的不同方法。例如:

  • 返回一个对象,则调用selectOne方法;

  • 返回List,则调用selectList;

  • 返回Map,则调用selectMap。

你可能会好奇,Mapper映射接口,是如何完成这些功能的。在接下来的内容中,笔者将从源码角度来分析Mybatis内部是如何使用JDK动态代理机制来完成这些功能,我们带着几个问题开始源码分析之旅:

  • SQL与Mapper接口的绑定关系是如何建立的?

  • 动态代理类是按照什么逻辑生成的?

  • 动态代理类是如何对方法进行拦截并处理的?

2 SQL与Mapper接口的绑定关系是如何建立的?

这个过程在mybatis初始化阶段,解析xml配置文件的时候就确定了。具体逻辑是,当解析一个xml配置文件时,会尝试根据<mapper namespace="....">的namespace属性值,判断classpath下有没有这样一个接口的全路径与namespace属性值完全相同,如果有,则建立二者之间的映射关系。

关解析代码位于XMLMapperBuilder的 parse方法中:

XMLMapperBuilder#parse

public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
   //根据namespace属性值,尝试绑定对应的Mapper接口
    bindMapperForNamespace();
  }
  ...
}

bindMapperForNamespace方法名,既可以看出来,其作用正是将Mapper映射器接口绑定到某个xml文件的namespace属性值。具体逻辑如下:

XMLMapperBuil

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值