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動態代理是基於接口生成一個代理實現類。

 

原創不易,有不正之處歡迎指正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM