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动态代理是基于接口生成一个代理实现类。

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

  • 相关阅读:
    Redis系列之一缓存穿透,缓存击穿,缓存雪崩
    centos 7 安装KVM
    windows 下免安装版本(zip archive)的 mysql 安装
    IDEA Scala 程序报错 Exception in thread "main" java.lang.NoClassDefFoundError: scala/Predef$
    Could not open client transport with JDBC Uri: jdbc:hive2://localhost:10000: Invalid status 16 (state=08S01,code=0)
    串口知识笔记
    C语言常用函数笔记
    流媒体服务器、海康威视 大华摄像头实现视频监控、直播解决方案(转)
    315M、433M和2.4G笔记
    MOS管开关电路笔记
  • 原文地址:https://www.cnblogs.com/teach/p/12755586.html
Copyright © 2011-2022 走看看