Mybatis源碼剖析 -- Mapper代理方式

一、回顧 Mapper 代理寫法

思考?個問題,通常的Mapper接口我們都沒有實(shí)現(xiàn)的方法卻可以使用,是為什么呢?
答案很簡單:動態(tài)代理
開始之前介紹?下 MyBatis 初始化時對接口的處理:MapperRegistry 是 Configuration 中的?個屬性,它內(nèi)部維護(hù)?個 HashMap 用于存放 mapper 接口的工廠類,每個接口對應(yīng)?個工廠類

/**
 * mapper代理方式
 */
@Test
public void test2() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = factory.openSession();
    // 使用JDK動態(tài)代理對mapper接口產(chǎn)生代理對象
    IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
    //代理對象調(diào)用接口中的任意方法,執(zhí)行的都是動態(tài)代理中的invoke方法
    List<User> all = mapper.findAll();
    for (User user : all) {
        System.out.println(user);
    }
}

mappers 標(biāo)簽中可以配置接口的包路徑,或者某個具體的接口類

<!--引入映射配置文件-->
<mappers>
    <package name="com.wujun.mapper"/>
</mappers>

二、初始化 Mapper

  1. 之前在說 Mybatis源碼剖析 -- 初始化過程(傳統(tǒng)方式)的時候,提到過 XMLConfigBuilder.parseConfiguration(XNode root)這個方法里面會對xml進(jìn)行解析,那么 mappers 的解析也在里面
    // 解析 <mappers /> 標(biāo)簽
    mapperElement(root.evalNode("mappers"));
    
    private void mapperElement(XNode parent) throws Exception {
            if (parent != null) {
                // 遍歷子節(jié)點(diǎn)
                for (XNode child : parent.getChildren()) {
                    // 如果是 package 標(biāo)簽,則掃描該包
                    if ("package".equals(child.getName())) {
                        // 獲取 <package> 節(jié)點(diǎn)中的 name 屬性
                        String mapperPackage = child.getStringAttribute("name");
                        // 從指定包中查找 mapper 接口,并根據(jù) mapper 接口解析映射配置
                        configuration.addMappers(mapperPackage);
                    // 如果是 mapper 標(biāo)簽,
                    } else {
                        // 獲得 resource、url、class 屬性
                        String resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
    
                        // resource 不為空,且其他兩者為空,則從指定路徑中加載配置
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            // 獲得 resource 的 InputStream 對象
                            InputStream inputStream = Resources.getResourceAsStream(resource);
                            // 創(chuàng)建 XMLMapperBuilder 對象
                            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                            // 執(zhí)行解析
                            mapperParser.parse();
                            // url 不為空,且其他兩者為空,則通過 url 加載配置
                        } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            // 獲得 url 的 InputStream 對象
                            InputStream inputStream = Resources.getUrlAsStream(url);
                            // 創(chuàng)建 XMLMapperBuilder 對象
                            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                            // 執(zhí)行解析
                            mapperParser.parse();
                            // mapperClass 不為空,且其他兩者為空,則通過 mapperClass 解析映射配置
                        } else if (resource == null && url == null && mapperClass != null) {
                            // 獲得 Mapper 接口
                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            // 添加到 configuration 中
                            configuration.addMapper(mapperInterface);
                            // 以上條件不滿足,則拋出異常
                        } else {
                            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                        }
                    }
                }
            }
        }
    
  2. ,調(diào)用configuration.addMapper(mapperPackage)方法將包名或者接口傳入 mapper 注冊表,存入一個名叫 knownMappers 的 HashMap 中,key就是接口的類型,value就是針對這個接口所生成的代理對象
    /**
     * Mapper 注冊表
     *
     * @author Clinton Begin
     * @author Eduardo Macarron
     * @author Lasse Voss
     */
    public class MapperRegistry {
    
        /**
         * MyBatis Configuration 對象
         */
        private final Configuration config;
        /**
         * MapperProxyFactory 的映射
         *
         * KEY:Mapper 接口
         */
        //這個類中維護(hù)一個HashMap存放MapperProxyFactory
        private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
    
  3. 接著看getMapper()的源碼
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        // 獲得 MapperProxyFactory 對象
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        // 不存在,則拋出 BindingException 異常
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        /// 通過動態(tài)代理工廠生成實(shí)例。
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }
    
    public T newInstance(SqlSession sqlSession) {
        // 創(chuàng)建了JDK動態(tài)代理的invocationHandler接口的實(shí)現(xiàn)類mapperProxy
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        // 調(diào)用了重載方法
        return newInstance(mapperProxy);
    }
    
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }
    
  4. invoke()方法源碼,其實(shí)本質(zhì)上還是調(diào)用了 sqlSession 里面的增刪改查方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果是 Object 定義的方法,直接調(diào)用
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
    
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // 獲得 MapperMethod 對象
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        // 重點(diǎn)在這:MapperMethod最終調(diào)用了執(zhí)行的方法
        return mapperMethod.execute(sqlSession, args);
    }
    
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        //判斷mapper中的方法類型,最終調(diào)用的還是SqlSession中的方法
        switch (command.getType()) {
            case INSERT: {
                // 轉(zhuǎn)換參數(shù)
                Object param = method.convertArgsToSqlCommandParam(args);
                // 執(zhí)行 INSERT 操作
                // 轉(zhuǎn)換 rowCount
                result = rowCountResult(sqlSession.insert(command.getName(), param));
                break;
            }
            case UPDATE: {
                // 轉(zhuǎn)換參數(shù)
                Object param = method.convertArgsToSqlCommandParam(args);
                // 轉(zhuǎn)換 rowCount
                result = rowCountResult(sqlSession.update(command.getName(), param));
                break;
            }
            case DELETE: {
                // 轉(zhuǎn)換參數(shù)
                Object param = method.convertArgsToSqlCommandParam(args);
                // 轉(zhuǎn)換 rowCount
                result = rowCountResult(sqlSession.delete(command.getName(), param));
                break;
            }
            case SELECT:
                // 無返回,并且有 ResultHandler 方法參數(shù),則將查詢的結(jié)果,提交給 ResultHandler 進(jìn)行處理
                if (method.returnsVoid() && method.hasResultHandler()) {
                    executeWithResultHandler(sqlSession, args);
                    result = null;
                // 執(zhí)行查詢,返回列表
                } else if (method.returnsMany()) {
                    result = executeForMany(sqlSession, args);
                // 執(zhí)行查詢,返回 Map
                } else if (method.returnsMap()) {
                    result = executeForMap(sqlSession, args);
                // 執(zhí)行查詢,返回 Cursor
                } else if (method.returnsCursor()) {
                    result = executeForCursor(sqlSession, args);
                // 執(zhí)行查詢,返回單個對象
                } else {
                    // 轉(zhuǎn)換參數(shù)
                    Object param = method.convertArgsToSqlCommandParam(args);
                    // 查詢單條
                    result = sqlSession.selectOne(command.getName(), param);
                    if (method.returnsOptional() &&
                            (result == null || !method.getReturnType().equals(result.getClass()))) {
                        result = Optional.ofNullable(result);
                    }
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + command.getName());
        }
        // 返回結(jié)果為 null ,并且返回類型為基本類型,則拋出 BindingException 異常
        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
            throw new BindingException("Mapper method '" + command.getName()
                    + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
        }
        // 返回結(jié)果
        return result;
    }
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容