聊聊Mybatis系列之Mapper接口
1.上期回顧
首先,我們還是回顧一下上篇文件的類容。先看下這個測試類,大家還有印象嗎:
- public class MybatisTest {
 - @Test
 - public void testSelect() throws IOException {
 - String resource = "mybatis-config.xml";
 - InputStream inputStream = Resources.getResourceAsStream(resource);
 - SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 - SqlSession session = sqlSessionFactory.openSession();
 - try {
 - FruitMapper mapper = session.getMapper(FruitMapper.class);
 - Fruit fruit = mapper.findById(1L);
 - System.out.println(fruit);
 - } finally {
 - session.close();
 - }
 - }
 - }
 
上篇源碼分析講了 mybatis 一級緩存的實(shí)現(xiàn)原理。這次,我們來了解下 mybatis 接口的創(chuàng)建。
2. mapper接口的創(chuàng)建流程
2.1 SqlSession的getMapper()
首先,我們來看下 FruitMapper mapper = session.getMapper(FruitMapper.class); 這段代碼,意思很簡單,根據(jù)傳入的class 獲取這個對象的實(shí)例。這個流程有點(diǎn)復(fù)雜,阿粉帶著大家來跟下源碼:
首先還是ctrl + 左鍵點(diǎn)擊 getMapper 方法,然后會進(jìn)入到 SqlSession 的 getMapper() 方法。然后之前阿粉也帶著大家了解了, SqlSession 的默認(rèn)實(shí)現(xiàn)類是 DefaultSqlSession ,所以我們直接看下 getMapper() 在 DefaultSqlSession 里面的實(shí)現(xiàn):
- @Override
 - public <T> T getMapper(Class<T> type) {
 - return configuration.getMapper(type, this);
 - }
 
2.2 Configuration 的getMapper()
這里從 configuration 里面去獲取, configuration 是全局配置對象,也就是上下文。參數(shù) this 是當(dāng)前的SqlSession 對象,繼續(xù)跟進(jìn)去看下:
- public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 - return mapperRegistry.getMapper(type, sqlSession);
 - }
 
2.3 MapperRegistry 的getMapper()
mapperRegistry 對象是干什么的呢?繼續(xù)點(diǎn)進(jìn)去:
- 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);
 - }
 - }
 
這里就不好看懂了,需要先看下了解下 MapperRegistry 這個類,我們一步一步來,跟著阿粉的思路走:
- public class MapperRegistry {
 - private final Configuration config;
 - private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
 - public MapperRegistry(Configuration config) {
 - this.config = config;
 - }
 - ...
 - }
 
了解一個類,首先看下成員變量和構(gòu)造方法。這里 config 不用多說了吧,主要的是 knownMappers 這個成員變量。這就是個map 對象,只是這個 map 對象的 value值是個對象,所以又要去看下 MapperProxyFactory 這個對象,點(diǎn)進(jìn)去:
- public class MapperProxyFactory<T> {
 - private final Class<T> mapperInterface;
 - private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
 - public MapperProxyFactory(Class<T> mapperInterface) {
 - this.mapperInterface = mapperInterface;
 - }
 - ...
 - }
 
首先,單獨(dú)看下這個類名 MapperProxyFactory ,取名是很有學(xué)問的,好的名字讓你一下就知道是干啥的。所以一看 MapperProxyFactory ,首先就會聯(lián)想到工廠模式,工廠模式是干啥的?創(chuàng)建對象的,創(chuàng)建什么對象呢?創(chuàng)建 MapperProxy 對象的。MapperProxy 也是有玄機(jī)的,Proxy 的是什么?看到這個一般都是使用代理模式來創(chuàng)建代理對象的。所以就很清楚了, MapperProxyFactory 這個類就是個工廠,創(chuàng)建的是 mapper 的代理對象。
然后這個類里面存的是 mapper 的接口和接口里面的方法。
最后,我們回到 MapperRegistry 類里面的 getMapper() 方法?,F(xiàn)在是不是要清楚一些,通過 mapper 接口去 map 里面獲取工廠類 MapperProxyFactory ,然后通過工廠類去創(chuàng)建我們的 mapper 代理對象。然后在看下 getMapper() 方法里面的 mapperProxyFactory.newInstance(sqlSession); 這段代碼,繼續(xù)點(diǎn)進(jìn)去:
- public T newInstance(SqlSession sqlSession) {
 - final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
 - return newInstance(mapperProxy);
 - }
 
你看,阿粉猜測對不對,MapperProxy 對象是不是出來了。然后看 newInstance() 這個方法:
- protected T newInstance(MapperProxy<T> mapperProxy) {
 - return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
 - }
 
兩個 newInstance() 方法都在MapperProxyFactory 這個類里面,這里就很明顯嘛。典型的 JDK 代理對象的創(chuàng)建。
好了,到這里我們的 mapper對象就獲取到了。大家可以想一想,為什么獲取一個 mapper 對象會那么復(fù)雜?或者說 mapper 對象有什么作用?其實(shí)就是為了通過 mapper 接口的方法獲取到 mapper.xml 里面的 sql,具體怎么獲取的,請允許阿粉賣個關(guān)子,請聽阿粉下回分解。
3.總結(jié)
最后,阿粉以一個時序圖來結(jié)束本篇文章,喜歡的話,記得點(diǎn)個贊哦。么么噠~
















 
 
 














 
 
 
 