开发者社区> code-x> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

MyBatis是如何让我们通过接口就能调用到SQL的

简介: MyBatis是如何让我们通过接口就能调用到SQL的
+关注继续查看

大致可分为如下几个步骤

1. 动态注册bean

1.1 根据配置mapperScan, 扫描对应的包, 将对应的类解析成BeanDefinition
1.2 通过替换BeanDefinition中的BeanClass为MapperFactoryBean, (原来的BeanClass是Mapper接口) 实现了在spring生成对应的对象时, 返回的对象不是本身类型的对象,而是MapperFactoryBean重写FactoryBean接口的getObject()方法返回的代理对象。该方法getObject()已经对mapper接口进行了代理, 即后续进行自动注入时, 也是返回getObject()生成的代理对象

2. 生成对应的代理对象

2.1 在getObject()方法中, 会获取到接口的全限定名称, 然后进一步对代理方法进行封装, 调用链如下
MapperFactoryBean: 
        public T getObject() throws Exception {
            return getSqlSession().getMapper(this.mapperInterface);
        }
       
       DefaultSqlSession: 
           public <T> T getMapper(Class<T> type) {
            //configuration是mybatis的重要配置类, 在初始化的时候, 就会将mapper接口添加到configuration中
            return configuration.getMapper(type, this);
           }
       
       Configuration: 
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            return mapperRegistry.getMapper(type, sqlSession);
        }
       
       MapperRegistry: 
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            //获取mapper代理工厂
            final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
            if (mapperProxyFactory == null) {
                throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
            }
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception e) {
                throw new BindingException("Error getting mapper instance. Cause: " + e, e);
            }
        }
       
       MapperProxyFactory: 
        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);
        }


2.2 在org.apache.ibatis.binding.MapperProxy#cachedInvoker中, new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())), 并且将代理方法调用器缓存起来. MapperMethod该对象即是最终调用方法的对象.

3. 执行对应的方法

3.1 方法调用, 代理里最常见的方法invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            //过滤掉object的方法
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else {
            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      }


3.2. org.apache.ibatis.binding.MapperProxy.PlainMethodInvoker重写的invoke方法中,判断该方法是否被调用过(是否存在于缓存), 若没有, 则创建一个PlainMethodInvoker方法调用器, 传入MapperMethod(MapperMethod是真正执行方法的对象), 并将新创建的PlainMethodInvoker存入缓存中(methodCache), 并调用该PlainMethodInvoker的invoke方法,
3.3. xml中的id和select等标签封装成了SqlCommand, 调用mapperMethod的execute, 执行对应的增删改查.

4. 结果集封装, 进行一些数据库数据对应java对象的转换

通过mybatis的封装和代理, 将mapper.xml转换成了接口的实例对象

如有谬误, 欢迎斧正

简化版如下: https://blog.csdn.net/sinat_25991865/article/details/89891581

public interface UserMapper {
    List<SysUser> selectAll();
}


public class MyMapperProxy<T> implements InvocationHandler {
    private Class<T> mapperInterface;
    private SqlSession sqlSession;
    
    public MyMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
            this.mapperInterface = mapperInterface;
            this.sqlSession = sqlSession;
    }
    @Override
    public Object invoke(Object proxy , Method method , Object[] args)
            throws Throwable {
        //针对不同的 sql 类型,需要调用sqlSession不同的方法
        //接口方法中的参数也有很多情况 ,这里只考虑没有有参数的情况
        List<T> list= sqlSession.selectList(
                mapperInterface.getCanonicalName() + ”.” + method.getName());
        //返回数据也有很多情况,这里不做处理直接返回
        return list;
    }
}


方法调用

//获取sqlSession
SqlSession sqlSession = getSqlSession();
//获取 UserMapper 接口
MyMapperProxy userMapperProxy = new MyMapperProxy(
        UserMapper.class , sqlSession) ;
UserMapper userMapper = (UserMapper) Proxy.newProxyinstance (
        Thread.currentThread().getContextClassLoader(),
        new Class[ ] {UserMapper.class},
        userMapperProxy) ;
//调 用 selectAll 方 法
List<SysUser> user= userMapper.selectAll();


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他(二)
MyBatis 是第一个支持自定义 SQL、存储过程和高级映射的类持久框架。MyBatis 消除了大部分 JDBC 的样板代码、手动设置参数以及检索结果。MyBatis 能够支持简单的 XML 和注解配置规则。使 Map 接口和 POJO 类映射到数据库字段和记录。
6 0
面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他(四)
MyBatis 是第一个支持自定义 SQL、存储过程和高级映射的类持久框架。MyBatis 消除了大部分 JDBC 的样板代码、手动设置参数以及检索结果。MyBatis 能够支持简单的 XML 和注解配置规则。使 Map 接口和 POJO 类映射到数据库字段和记录。
7 0
面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他(四)
MyBatis 是第一个支持自定义 SQL、存储过程和高级映射的类持久框架。MyBatis 消除了大部分 JDBC 的样板代码、手动设置参数以及检索结果。MyBatis 能够支持简单的 XML 和注解配置规则。使 Map 接口和 POJO 类映射到数据库字段和记录。
23 0
Mybatis--特殊SQL的执行
mybatis特殊SQL的执行,里面包含了使用Mybatis的各种小细节
37 0
mybatis系列之初识mybatis
今天开始,阿粉准备把 mybatis 的知识梳理一遍,为什么梳理 mybatis 呢?因为 mybatis 的源码最简单啊(说什么大实话)。no~no~no~,是因为现在的这些框架都和 springboot 整合在一起了,用起来是方便了,但是其中的原理就越不了解了。所以阿粉整理几篇 mybatis 的文章分享给大家,配合代码案例,希望大家有所收获。另外因为这是第一篇,所以代码量相对来说比较多,希望大家耐心看下去,毕竟阿粉也是下了一番功夫的,绝对是原创,如有雷同,肯定是他们抄袭阿粉的。 好了,话不多说,我们直接进入正题。
44 0
SQL--( 初识 MyBatis)
MyBatis介绍 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
1433 0
MyBatis开发步骤
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。
941 0
mybatis 使用经验小结
一、多数据源问题 主要思路是把dataSource、sqlSesstionFactory、MapperScannerConfigurer在配置中区分开,各Mapper对应的包名、类名区分开 1 2 13 14 16 17 ...
965 0
+关注
14
文章
1
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载