聊聊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 一級緩存的實現原理。這次,我們來了解下 mybatis 接口的創建。
2. mapper接口的創建流程
2.1 SqlSession的getMapper()
首先,我們來看下 FruitMapper mapper = session.getMapper(FruitMapper.class); 這段代碼,意思很簡單,根據傳入的class 獲取這個對象的實例。這個流程有點復雜,阿粉帶著大家來跟下源碼:
首先還是ctrl + 左鍵點擊 getMapper 方法,然后會進入到 SqlSession 的 getMapper() 方法。然后之前阿粉也帶著大家了解了, SqlSession 的默認實現類是 DefaultSqlSession ,所以我們直接看下 getMapper() 在 DefaultSqlSession 里面的實現:
- @Override
- public <T> T getMapper(Class<T> type) {
- return configuration.getMapper(type, this);
- }
2.2 Configuration 的getMapper()
這里從 configuration 里面去獲取, configuration 是全局配置對象,也就是上下文。參數 this 是當前的SqlSession 對象,繼續跟進去看下:
- public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
- return mapperRegistry.getMapper(type, sqlSession);
- }
2.3 MapperRegistry 的getMapper()
mapperRegistry 對象是干什么的呢?繼續點進去:
- 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;
- }
- ...
- }
了解一個類,首先看下成員變量和構造方法。這里 config 不用多說了吧,主要的是 knownMappers 這個成員變量。這就是個map 對象,只是這個 map 對象的 value值是個對象,所以又要去看下 MapperProxyFactory 這個對象,點進去:
- 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;
- }
- ...
- }
首先,單獨看下這個類名 MapperProxyFactory ,取名是很有學問的,好的名字讓你一下就知道是干啥的。所以一看 MapperProxyFactory ,首先就會聯想到工廠模式,工廠模式是干啥的?創建對象的,創建什么對象呢?創建 MapperProxy 對象的。MapperProxy 也是有玄機的,Proxy 的是什么?看到這個一般都是使用代理模式來創建代理對象的。所以就很清楚了, MapperProxyFactory 這個類就是個工廠,創建的是 mapper 的代理對象。
然后這個類里面存的是 mapper 的接口和接口里面的方法。
最后,我們回到 MapperRegistry 類里面的 getMapper() 方法。現在是不是要清楚一些,通過 mapper 接口去 map 里面獲取工廠類 MapperProxyFactory ,然后通過工廠類去創建我們的 mapper 代理對象。然后在看下 getMapper() 方法里面的 mapperProxyFactory.newInstance(sqlSession); 這段代碼,繼續點進去:
- 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 代理對象的創建。
好了,到這里我們的 mapper對象就獲取到了。大家可以想一想,為什么獲取一個 mapper 對象會那么復雜?或者說 mapper 對象有什么作用?其實就是為了通過 mapper 接口的方法獲取到 mapper.xml 里面的 sql,具體怎么獲取的,請允許阿粉賣個關子,請聽阿粉下回分解。
3.總結
最后,阿粉以一個時序圖來結束本篇文章,喜歡的話,記得點個贊哦。么么噠~