MyBatis 学习记录2 Mapper对象是如何生成的
主题
以前我一直有一个问题不懂.并且觉得很神奇.就是Mybatis我们开发的时候只需要定义接口,并没有写实现类,为什么我们运行的时候就可以直接使用? 现在我想分享下这部分大致是怎么实现的.
在启动的时候

根据之前的分享,在初始化阶段Build SqlSessionFactory的时候需要用到XMLConfigBuilder去parse XML文件生成Configuration对象,在 parse的步骤中其中有一步就是parse mappers节点


在parse mapper过程中会用到XMLMapperBuilder去parse.一步一步进入断点.

会发现后面会调用configuration的addMapper方法.它会调用MapperRegistry的addMapper方法

MapperRegistry相当用knownMappers这个hashmap于为每个Mapper注册一次,其中key是你自定义的Mapper接口的class,Value是MapperProxyFactory类的对象.
Factory一个就是一个工厂类,它肯定需要生产对应的对象,从名字上也能发现它生产的就是MapperProxy
/**
* Copyright 2009-2015 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.binding; import org.apache.ibatis.session.SqlSession; import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* @author Lasse Voss
*/
public class MapperProxyFactory<T> { private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
} public Class<T> getMapperInterface() {
return mapperInterface;
} public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
} @SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
} public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
} }
/**
* Copyright 2009-2015 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.binding; import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession; import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map; /**
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
} private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
} }
从代码中我们可以得知,这里是用了JDK动态代理,MapperProxy类implements了InvocationHandler,
如果是调用自定义Mapper的Object类中的方法,比如toString,那就直接调用,否则的话调用mapperMethod.execute去执行对应的方法(比如selectById).那么mapperMethod是什么呢?
这个类的对象其实就是对应你写的Mapper里的方法,你的每个方法对应1个MapperMethod,相当于是Java的Method的包装.
另外还包含了你在XML里定义的SQL字符串, 是select还是insert还是update,delete操作等信息.相当于融合了你定义的Mapper里的Method和你为每个Method在XML里的写的信息.
这样你调用mapperMethod.execute的时候就能找到对应的SQL去执行了.
通过SqlSession获取Mapper
初始化完成后就如同之前的介绍,会在confuguration的mapperRegistry里注册好了各种MapperFactory.那么通过SqlSession去获取Mapper的时候也是类似的.会调用configuration去获取Mapper,内部会调用mapperRegistry去获取Mapper


然后通过MapperProxyFactory去创建一个MapperProxy并返还


小结
1.说白了,其实就是利用JDK动态代理,返回给你1个实现了你写的Mapper接口的对象,而其中的Invocation接口的实现类就是MapperProxy.
2.在初始化阶段会为你写的每个Mapper在Configuration的MapperRegistry里注册一个MapperFactory,当你要获取Mapper实例的时候就通过这个Factory来new.
3.当你调用Mapper.XXX方法的时候,比如select,就会调用MapperProxy的invoke方法,获取你定义在Mapper里的xxx方法对应的MapperMethod对象,这个对象就是Method的封装,同时在XML里找到对应的select语句再执行.
4.你写的每个Mapper类的对象对应1个MapperProxyFactory生成1个MapperProxy,你在Mapper中定义的每个方法对应1个MapperMethod,它是Java的Method的封装
MyBatis 学习记录2 Mapper对象是如何生成的的更多相关文章
- MyBatis 学习记录5 MyBatis的二级缓存
主题 之前学习了一下MyBatis的一级缓存,主要涉及到BaseExecutor这个类. 现在准备学习记录下MyBatis二级缓存. 配置二级缓存与初始化发生的事情 首先二级缓存默认是不开启的,需要自 ...
- MyBatis 学习记录3 MapperMethod类
主题 之前学习了一下MapperProxy的生产过程,自定义Mapper类的对象是通过动态代理生产的,调用自定义方法的时候实际上是调用了MapperMethod的execute方法:mapperMet ...
- mybatis 学习记录1
起因 以前刚学习java三大框架的时候持久层框架我是自学的是hibernate..感觉蛮好用的,so easy..后来大三实习公司用的是jpa(hibernate外包装一层)...再后来工作1年多用的 ...
- MyBatis 学习记录7 一个Bug引发的思考
主题 这次学习MyBatis的主题我想记录一个使用起来可能会遇到,但是没有经验的话很不好解决的BUG,在特定情况下很容易发生. 异常 java.lang.IllegalArgumentExceptio ...
- MyBatis 学习记录6 TypeHandler
主题 因为对MyBatis在JDBC数据和Java对象之间数据转化比较感兴趣,所以就记录并学习一下TypeHandler. 使用场景 如上图所示,观察下接口方法就能明白.TypeHandler主要用于 ...
- MyBatis 学习记录4 MyBatis的一级缓存
主题 分享记录一下MyBatis的一级缓存相关的学习. Demo public static void firstLevelCache() { init("mybatis-config.xm ...
- mybatis学习记录六——一对一、一对多和多对多查询
9 订单商品数据模型 9.1 数据模型分析思路 1.每张表记录的数据内容 分模块对每张表记录的内容进行熟悉,相当 于你学习系统 需求(功能)的过程. 2.每张表重要的字段设置 非空 ...
- Mybatis学习记录(六)----Mybatis的高级映射
1.一对多查询 1.1 需求 查询订单及订单明细的信息. 1.2 sql语句 确定主查询表:订单表 确定关联查询表:订单明细表 在一对一查询基础上添加订单明细表关联即可. SELECT orders. ...
- Mybatis学习记录(五)----Mybatis的动态SQL
1. 什么是动态sql mybatis核心 对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接.组装. 1.1 需求 用户信息综合查询列表和用户信息查询列表总数这两个statemen ...
随机推荐
- sublime text3安装以及插件配置教程
http://blog.csdn.net/feizaosyuacm/article/details/54729891 本文是安装的Sublime Text3是portable version(可移动版 ...
- Python学习-购物车程序
程序:购物车程序 需求: 启动程序后,让用户输入工资,然后打印商品列表 允许用户根据商品编号购买商品 用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒 可随时退出,退出时,打印已购买商品和余额 ...
- spring事务传播机制的测试结果
/** * @Component是个一般性的注解,使用此注解修饰的POJO类,有value属性,指定bean的id.也可不写.默认值是类名首字母小写 * @Resource是控制依赖注 ...
- __all__ 作用, 相当于导入*
它是一个string元素组成的list变量,定义了当你使用 from <module> import * 导入某个模块的时候能导出的符号(这里代表变量,函数,类等) 参考文章: http: ...
- 基于openresty 的几个开发框架
1. kong api gateway Github: https://github.com/Mashape/kong 2. Lapis web 开发框架 Github: ...
- js核心知识
枚举属性: var o = {x:1} console.log("x" in o);//true console.log("toString" in o);// ...
- qqbot 配置
qqbot 配置 用起来还是挺方便的,使用 pip install qqbot 就可以. 不过找配置文件没注意,以为是在程序目前,原来是在 C:\Users\xxx.qqbot-tmp 目录. 插件可 ...
- python官网
https://www.python.org/ https://docs.python.org/2/library/pydoc.html
- 如何在 Linux 上使用 x2go 设置远程桌面
https://linux.cn/article-5708-1.html 由于一切都迁移到了云上,作为提高职员生产力的一种方式,虚拟远程桌面在工业中越来越流行.尤其对于那些需要在多个地方和设备之间不停 ...
- 揭晓UX(用户体验)最大的秘密
我是佩恩和特勒的粉丝已经多年了.我第一次在现实中看到他们是在上个月,被他们的表演完全迷住了. 我真的很喜欢佩恩和特勒,他们经常“回拉窗帘”,并揭示他们是怎么完成他们的魔法.其他魔术师营造神秘主义和虚假 ...