MyBatis源碼解讀(2)——MapperProxy

SqlSession能夠說是整個MyBatis的重中之重,在SqlSession中涉及到前一篇四大對象:Executor、StatementHandler、ParameterHandler、ResultHandler,因此在此先只對SqlSession有一個大概的瞭解。sql

在代碼中咱們能夠看到當咱們構造出一個SqlSession實例事後,能夠經過SqlSession構造出Mappper映射器。UserMapper是一個接口,那麼咱們能夠確定的是,它必定是用了Java的動態代理生成了一個代理類。apache

SqlSession sqlSession = SessionFactory.getSqlSession(resource); 
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

接着上一篇講到經過DefaultSqlSessionFactory能夠獲得一個SqlSession的實例DefaultSqlSession。session

經過源代碼能夠發現,SqlSessionManger又實現了SqlSession,在上一節中可知SqlSessionManager一樣也繼承了SqlSessionFactory接口。咱們把它們結合起來看看。mybatis

 

看來這個SqlSessionManager有點神奇,同時繼承SqlSession和SqlSessionFactory。從名字上來看好像是管理SqlSession的,這裏不討論,隨着源代碼的閱讀相信咱們能逐步清晰,包括整個包的結構。app

回到DefaultSqlSession中來。在SqlSession接口中提供了不少方法,用於咱們的增刪改查,這在舊版的MyBatis或者iBatis中經常所使用的,咱們如今大多直接使用xml配置文件以達到更加靈活的效果。因此咱們將注意力放在getMapper方法上。ide

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

注意UserMapper僅僅是一個接口,這裏會涉及到Java的動態代理,因此得要有必定的基礎才能讀懂。this

經過打斷點調試咱們能夠發現確實產生了一個叫MapperProxy的代理類。spa

下面是DefaultSqlSession的getMapper方法:代理

//org.apache.ibatis.session.default.DefaultSqlSession
public
<T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }

看起來語法有點奇怪,這是一個泛型方法。看來是調用了Configuration的getMapper方法,還不是DefaultSqlSession實現了getMapper。接着再看Configuration的getMapper方法:調試

//org.apache.ibatis.session.Configuration
public
<T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }

 Configuration.getMapper一共兩個參數,一個是Class類型,一個是SqlSession,在DefaultSqlSession.getMapper調用Configuration.getMapper時,將傳遞進來的Class類型參數和其自己傳遞給了Configuration.getMapper。此時還不是在Configuration中實現了getMapper,看來仍是一個叫作mapperRegistry的變量。

//org.apache.ibatis.session.Configuration
protected
final MapperRegistry mapperRegistry = new MapperRegistry(this);

看着名字好像是註冊Mapper映射器的地方,想來也是,既然要獲得Mapper的映射,那麼全部的Mapper都要一個地方去註冊(在咱們的mybytis-config.xml裏),註冊好事後須要的時候再去查找是否已經註冊,那麼就是MapperRegistry,因此取一個好的變量名是很是重要的。

 1 //org.apache.ibatis.binding.MapperRegistry
 2 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 3   final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
 4   if (mapperProxyFactory == null) {
 5     throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 6   }
 7   try {
 8     return mapperProxyFactory.newInstance(sqlSession);
 9   } catch (Exception e) {
10     throw new BindingException("Error getting mapper instance. Cause: " + e, e);
11   }
12 }

在第3行代碼中試圖從一個叫knownMappers的變量取出MapperProxyFactory。這個knownMapper的定義:

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

既然能用get方法取,那說明就有add方法咯?果不其然咱們在MapperRegistry類中發現了public <T> void addMapper(Class<T> type)方法,那麼是在哪裏調用的這個方法呢?

咱們來從新理一理。

使用MyBatis的第一步是配置mybatis-config.xml,配置好事後,mybatis-config跑起來的第一步也必定是首先解析xml配置文件,將解析好的配置文件各個配置參數放入Configuration對象中,包括Mapper的配置,因此應該是在解析xml文件的某個類中解析過來後調用Configuration的方法將mapper放置到MapperRegister中。事實也的確如此,有興趣能夠跟蹤下代碼看看。回到MapperRegistry.getMapper的方法中。

當咱們一切正確時,咱們就能獲取到一個MapperProxyFactory實例。想必MapperProxy代理類的生成正是經過MapperProxyFactory工廠類構建的,即第8行代碼。進入MapperProxyFactory類。

//org.apache.ibatis.binding.MapperProxyFactory
public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

在這裏終於看到了MapperProxy代理類,是經過sqlSession、mapperInterface、mechodCache三個參數構造的。

newInstance有一個重載方法:

//org.apache.ibatis.binding.MapperProxyFactory
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

終因而走到頭了,這裏就是返回的一個代理類實例。最後來看看MapperProxy。

MapperProxy是一個重要的類,因此咱們將其代碼所有貼出:

 1 //org.apache.ibatis.binding.MapperProxy
 2 public class MapperProxy<T> implements InvocationHandler, Serializable {
 3 
 4   private static final long serialVersionUID = -6424540398559729838L;
 5   private final SqlSession sqlSession;
 6   private final Class<T> mapperInterface;
 7   private final Map<Method, MapperMethod> methodCache;
 8 
 9   public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
10     this.sqlSession = sqlSession;
11     this.mapperInterface = mapperInterface;
12     this.methodCache = methodCache;
13   }
14 
15   @Override
16   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
17     if (Object.class.equals(method.getDeclaringClass())) {
18       try {
19         return method.invoke(this, args);
20       } catch (Throwable t) {
21         throw ExceptionUtil.unwrapThrowable(t);
22       }
23     }
24     final MapperMethod mapperMethod = cachedMapperMethod(method);
25     return mapperMethod.execute(sqlSession, args);
26   }
27 
28   private MapperMethod cachedMapperMethod(Method method) {
29     MapperMethod mapperMethod = methodCache.get(method);
30     if (mapperMethod == null) {
31       mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
32       methodCache.put(method, mapperMethod);
33     }
34     return mapperMethod;
35   }
36 
37 }

要使用Java的動態代理就必須得實現InvocationHandler接口,在第17行代碼中首先判斷代理對象是一個接口仍是一個類,顯然咱們沒有對mapper接口進行任何實現,那麼它將生成一個MapperMethod對象,接着調用其execute方法,把sqlSession和參數傳遞進去。

相關文章
相關標籤/搜索