zoukankan      html  css  js  c++  java
  • mybatis源码分析:Mapper接口是什么

    在《mybatis源码分析:启动过程》中分析了mybatis的启动过程,mybatis的启动过程主要集中在解析其核心配置文件(mybatis-config.xml)上,把配置文件中的配置全部解析到Configuration类中,每个配置在Configuration中均能找到其设置。本文分析mybatis中的查询接口(例,UserMapper)。

    一、概述

    在编写mybatis的程序时,常见的做法时编写一个Mapper接口,再编写相应的映射文件,之后便可以初始化mybatis的环境,调用该接口的方法执行操作数据库的各中操作。那么Mapper接口是什么对象那,是怎样和映射文件关联,最后又怎样执行方法的。今天先分析Mapper接口,具体的环境可参考《mybatis源码分析:启动过程》中的相关代码。这里先给出一个结论,Mapper接口是使用JDK代理生成的一个代理类。

    二、详述

    上面说到Mapper接口是使用JDK动态代理生成的一个代理类,下面通过代码去分析,下面是我测试的代码片段,

    package cn.com.mybatis.test;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.List;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import cn.com.mybatis.dao.UserMapper;
    
    public class TestMybatis {
    
        public static void main(String[] args) throws IOException {
            // TODO Auto-generated method stub
    
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            System.out.println(ClassLoader.getSystemClassLoader());
            System.out.println(Resources.class.getClassLoader());
            SqlSessionFactoryBuilder  builder  = new SqlSessionFactoryBuilder();
            SqlSessionFactory  factory  = builder.build(inputStream);
            SqlSession session=factory.openSession();
            UserMapper userMapper=session.getMapper(UserMapper.class);
    //        session.insert("");
            System.out.println("userMapper:"+userMapper);
           
            
            userMapper.getUser();
            
        }
    
    }

    上面从SqlSession中调用getMapper方法,传入一个XXMapper.class的参数,这里传入UserMapper.class(必须是接口 ?),返回的是一个UserMapper的对象,那么这个返回的值到底是什么那,我们分析源码。我们值得这里的session是一个DefaultSqlSession对象,看其方法,

    @Override
      public <T> T getMapper(Class<T> type) {
        return configuration.<T>getMapper(type, this);
      }

    调用了Configuration对象的getMapper方法,

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
      }

    调用了mapperRegistry的getMapper方法,

    @SuppressWarnings("unchecked")
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
          //根据Class对象获得一个MapperProxyFactory
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
            //生成一个目标对象的JDK代理对象,并返回
          return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
      }

    从上面可以看到从knownsMappers中根据type返回一个MapperProxyFactory,直译过来就是Mapper接口的代理工厂,mybatis的无处不在的工厂模式啊。

    下面看knownsMappers的定义,

    从上面可以看出knownMappers中使用map存储,其值为MapperProxyFactory,看下MapperProxyFactory的定义,

    从上面可以看到该类有一个构造方法,该构造方法的参数也是一个Class对象,参数名称为mapperInterface,从方法名直译过来就是我们写的Mapper接口。

    上面了解了knownsMappers和MapperProxyFactory后,可以知道knownsMappers中的value是根据Mapper接口生成的,那么取出来的值必为当前key的一个MapperProxyFactory,即该对象中的mapperInterface为key值。

    根据Class取出其对应的MapperProxyFactory后,下面调用其newInstance方法,如下

    下面看其newInstance方法,

    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
      }

    生成一个MapperProxy对象,

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
      }

    上面仅是给三个字段赋值,再看后面的方法,MapperProxyFactory的newInstance(mapperProxy)方法,

    @SuppressWarnings("unchecked")
      protected T newInstance(MapperProxy<T> mapperProxy) {
          //JDK动态代理
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }

    从上面可以看到是标准的JDK动态代理生成对象的放式,不再赘述。从这里看出返回的是一个Mapper接口的动态代理对象,第三个参数mapperProxy必然实现了InvocationHandler接口,看其定义

    可以看到实现了InvocationHandler接口,那么在调用Mapper接口的时候肯定会执行MapperProxy的invoke方法,这里执行过程下次再分析。

    三、总结

    本文分析了mybatis中Mapper接口的类型,即从mybatis中取出时是什么类型,这里是一个JDK的动态代理,所以我们要写的是一个接口,因为JDK动态代理是基于接口生成一个代理实现类。

    原创不易,有不正之处欢迎指正。

  • 相关阅读:
    第一节:SpringMVC概述
    SpringMVC【目录】
    Windows 系统快速查看文件MD5
    (error) ERR wrong number of arguments for 'hmset' command
    hive使用遇到的问题 cannot recognize input
    Overleaf支持的部分中文字体预览
    Understanding and Improving Fast Adversarial Training
    Django2实战示例 第十三章 上线
    Django2实战示例 第十二章 创建API
    Django2实战示例 第十一章 渲染和缓存课程内容
  • 原文地址:https://www.cnblogs.com/teach/p/12755586.html
Copyright © 2011-2022 走看看