谈谈 MyBatis 中 Mapper 接口方法的执行过程

你是否也曾好奇MyBatis中的Mapper当中的方法是如何被执行?这篇文章通过源码解析其中的流程。

概述

MyBatis通过JDK动态代理的方式创建Mapper接口的代理对象,当Mapper代理对象调用Mapper接口中定义的方法时,会调用InvocationHandler中的invoke()方法,执行相应的拦截逻辑,从而完成数据库操作。

执行过程

我们知道Mapper由两部分组成:Mapper接口和通过注解或者XML文件配置的SQL语句。以下通过一个单元测试的代码来看如何执行Mapper中定义的方法:

//加载主配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml")
//通过主配置文件信息构建 sqlSessionFactor,在这里会加载Mapper的配置信息:调用MapperRegistry类中的addMapper方法将信息放入缓存中
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
//获取SqlSession实例
SqlSession session = sqlMapper.openSession()
//通过动态代理获取Mapper代理对象
AuthorMapper mapper = session.getMapper(AuthorMapper.class);
//执行Mapper中的方法:会被代理类中的invoke方法拦截
List<Author> authors = mapper.selectAllAuthors();

跟踪源码,可以看到完整的流程:

1、DefaultSqlSession.java: SqlSession接口的默认实现类
//SqlSession中定义了获取Mapper对象的方法,DefaultSqlSession中实现了这个方法:实际上是调用Configuration类中getMapper方法
public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
}
2、Configuration.java: 用于封装配置文件信息的类
//Mapper注册类实例
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}
3、MapperRegistry.java:Mapper信息注册和获取
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    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);
    }
}
4、MapperProxyFactory.java:Mapper代理工厂类
//通过 Proxy.newProxyInstance 生成代理对象
public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
    //通过Proxy类获取代理对象实例
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

总结一下:MyBatis框架在应用启动时会解析所有的Mapper接口,然后调用MapperRegistry对象的addMapper()方法将Mapper接口信息和对应的MapperProxyFactory(Mapper代理工厂类)对象注册到knownMappers缓存中。所以可以直接从缓存中获取Mapper接口类型对应MapperProxyFactory对象,从而构造出MapperProxy实例。

通用的执行逻辑

在MapperProxy类中定义了通用的执行逻辑,当调用Mapper当中的方法时便会执行这些逻辑:MapperProxy中的invoke方法:

@Override
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 {
	//注:cachedInvoker(method).invoke会执行 mapperMethod.execute(sqlSession, args)方法,完成对数据库的操作
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
}

个人博客:https://www.kangpeiqin.cn/#/index
欢迎与我交流,关注公众号,获取更多技术相关知识:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值