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

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