前言

大家使用MyBatis都知道,不管是单独使用还是和Spring集成,我们都是使用接口定义的方式声明数据库的增删改查方法。那么我们只声明一个接口,MyBatis是如何帮我们来实现SQL呢,对吗,我们的sql是定义在/resources/mapper/mybatis下。每个单独的xml文件都有一个id和接口里的方法一一对应。这里的对应关系就是mybatis框架帮我们做的工作。

这里的关键类分为两部分:SqlSessionFactory、SqlSession、MapperProxy。

1、MapperProxy

这个类乍一看有点陌生,那我们就先从调用入口出发来分析这个类。

 SqlSession sqlSession=sqlSessionFactory.openSession();StudentDao studentDao=sqlSession.getMapper(StudentDao.class);Student student=studentDao.getStudent(1);

 这里看到调用sqlSession.getMapper

public <T> T getMapper(Class<T> type) {return this.configuration.getMapper(type, this);
}

 SqlSession本身不做任何事情,直接把任务甩给Configuration。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return this.mapperRegistry.getMapper(type, sqlSession);
}

 Configuration又把任务甩给MapperRegistry。 从类名就能看出来它的作用是用来注册接口和生成代理类实例的工具类。 从getMapper里看到先获取MapperProxyFactory,如果未空则抛异常。然后继续调用mapperProxyFactory.newInstance()。

这里我们看到mybatis中变量太多的话可以使用var1、var2。。。是个好办法哦。

public class MapperRegistry {private final Configuration config;private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();public MapperRegistry(Configuration config) {this.config = config;}public <T> T getMapper(Class<T> type, SqlSession sqlSession) {MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);if(mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");} else {try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception var5) {throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);}}}public <T> boolean hasMapper(Class<T> type) {return this.knownMappers.containsKey(type);}public <T> void addMapper(Class<T> type) {if(type.isInterface()) {if(this.hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {this.knownMappers.put(type, new MapperProxyFactory(type));MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);parser.parse();loadCompleted = true;} finally {if(!loadCompleted) {this.knownMappers.remove(type);}}}}public Collection<Class<?>> getMappers() {return Collections.unmodifiableCollection(this.knownMappers.keySet());}public void addMappers(String packageName, Class<?> superType) {ResolverUtil resolverUtil = new ResolverUtil();resolverUtil.find(new IsA(superType), packageName);Set mapperSet = resolverUtil.getClasses();Iterator var5 = mapperSet.iterator();while(var5.hasNext()) {Class mapperClass = (Class)var5.next();this.addMapper(mapperClass);}}public void addMappers(String packageName) {this.addMappers(packageName, Object.class);}
}

  MapperProxyFactory是创建Mapper代理类的工厂类。 这个类里看到两个newInstance方法。第一个是直接创建一个代理类并返回。第二类是创建一个MapperProxy类然后调用第一个newInstance方法。这里绕了一大圈,终于看到MapperProxy的影子了。调用链实在太长,继续往下看。

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;}public Class<T> getMapperInterface() {return this.mapperInterface;}public Map<Method, MapperMethod> getMethodCache() {return this.methodCache;}protected T newInstance(MapperProxy<T> mapperProxy) {return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);}public T newInstance(SqlSession sqlSession) {MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);return this.newInstance(mapperProxy);}
}

  MapperProxy这个类实现了jdk动态代理接口InvocationHandler。在invoke方法中实现代理方法调用的细节。 到这里说它是关键类还没看到有sql蛛丝马迹的地方。那只能继续往下看,这里看到在invoke方法里先获取MapperMethod类,然后调用mapperMethod.execute()。

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;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if(Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}if(this.isDefaultMethod(method)) {return this.invokeDefaultMethod(proxy, method, args);}} catch (Throwable var5) {throw ExceptionUtil.unwrapThrowable(var5);}MapperMethod mapperMethod = this.cachedMapperMethod(method);return mapperMethod.execute(this.sqlSession, args);}private MapperMethod cachedMapperMethod(Method method) {MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);if(mapperMethod == null) {mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());this.methodCache.put(method, mapperMethod);}return mapperMethod;}@UsesJava7private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {Constructor constructor = Lookup.class.getDeclaredConstructor(new Class[]{Class.class, Integer.TYPE});if(!constructor.isAccessible()) {constructor.setAccessible(true);}Class declaringClass = method.getDeclaringClass();return ((Lookup)constructor.newInstance(new Object[]{declaringClass, Integer.valueOf(2)})).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);}private boolean isDefaultMethod(Method method) {return (method.getModifiers() & 1033) == 1 && method.getDeclaringClass().isInterface();}
}

  MapperMethod类是整个代理机制的核心类,对SqlSession中的操作进行了封装。 该类里有两个内部类SqlCommand和MethodSignature。 SqlCommand用来封装增删改查操作,也就是我们在xml中配置的select、update、delete、insert节点。每个节点都会生成一个MappedStatement类。MethodSignature用来封装方法的参数,返回类型。

public class MapperMethod {private final MapperMethod.SqlCommand command;private final MapperMethod.MethodSignature method;public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);}public Object execute(SqlSession sqlSession, Object[] args) {Object param;Object result;switch(null.$SwitchMap$org$apache$ibatis$mapping$SqlCommandType[this.command.getType().ordinal()]) {case 1:param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));break;case 2:param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.update(this.command.getName(), param));break;case 3:param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));break;case 4:if(this.method.returnsVoid() && this.method.hasResultHandler()) {this.executeWithResultHandler(sqlSession, args);result = null;} else if(this.method.returnsMany()) {result = this.executeForMany(sqlSession, args);} else if(this.method.returnsMap()) {result = this.executeForMap(sqlSession, args);} else if(this.method.returnsCursor()) {result = this.executeForCursor(sqlSession, args);} else {param = this.method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(this.command.getName(), param);}break;case 5:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + this.command.getName());}if(result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {throw new BindingException("Mapper method \'" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");} else {return result;}}private Object rowCountResult(int rowCount) {Object result;if(this.method.returnsVoid()) {result = null;} else if(!Integer.class.equals(this.method.getReturnType()) && !Integer.TYPE.equals(this.method.getReturnType())) {if(!Long.class.equals(this.method.getReturnType()) && !Long.TYPE.equals(this.method.getReturnType())) {if(!Boolean.class.equals(this.method.getReturnType()) && !Boolean.TYPE.equals(this.method.getReturnType())) {throw new BindingException("Mapper method \'" + this.command.getName() + "\' has an unsupported return type: " + this.method.getReturnType());}result = Boolean.valueOf(rowCount > 0);} else {result = Long.valueOf((long)rowCount);}} else {result = Integer.valueOf(rowCount);}return result;}private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(this.command.getName());if(Void.TYPE.equals(((ResultMap)ms.getResultMaps().get(0)).getType())) {throw new BindingException("method " + this.command.getName() + " needs either a @ResultMap annotation, a @ResultType annotation, or a resultType attribute in XML so a ResultHandler can be used as a parameter.");} else {Object param = this.method.convertArgsToSqlCommandParam(args);if(this.method.hasRowBounds()) {RowBounds rowBounds = this.method.extractRowBounds(args);sqlSession.select(this.command.getName(), param, rowBounds, this.method.extractResultHandler(args));} else {sqlSession.select(this.command.getName(), param, this.method.extractResultHandler(args));}}}private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {Object param = this.method.convertArgsToSqlCommandParam(args);List result;if(this.method.hasRowBounds()) {RowBounds rowBounds = this.method.extractRowBounds(args);result = sqlSession.selectList(this.command.getName(), param, rowBounds);} else {result = sqlSession.selectList(this.command.getName(), param);}return !this.method.getReturnType().isAssignableFrom(result.getClass())?(this.method.getReturnType().isArray()?this.convertToArray(result):this.convertToDeclaredCollection(sqlSession.getConfiguration(), result)):result;}private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {Object param = this.method.convertArgsToSqlCommandParam(args);Cursor result;if(this.method.hasRowBounds()) {RowBounds rowBounds = this.method.extractRowBounds(args);result = sqlSession.selectCursor(this.command.getName(), param, rowBounds);} else {result = sqlSession.selectCursor(this.command.getName(), param);}return result;}private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {Object collection = config.getObjectFactory().create(this.method.getReturnType());MetaObject metaObject = config.newMetaObject(collection);metaObject.addAll(list);return collection;}private <E> Object convertToArray(List<E> list) {Class arrayComponentType = this.method.getReturnType().getComponentType();Object array = Array.newInstance(arrayComponentType, list.size());if(!arrayComponentType.isPrimitive()) {return list.toArray((Object[])((Object[])array));} else {for(int i = 0; i < list.size(); ++i) {Array.set(array, i, list.get(i));}return array;}}private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {Object param = this.method.convertArgsToSqlCommandParam(args);Map result;if(this.method.hasRowBounds()) {RowBounds rowBounds = this.method.extractRowBounds(args);result = sqlSession.selectMap(this.command.getName(), param, this.method.getMapKey(), rowBounds);} else {result = sqlSession.selectMap(this.command.getName(), param, this.method.getMapKey());}return result;}public static class MethodSignature {private final boolean returnsMany;private final boolean returnsMap;private final boolean returnsVoid;private final boolean returnsCursor;private final Class<?> returnType;private final String mapKey;private final Integer resultHandlerIndex;private final Integer rowBoundsIndex;private final ParamNameResolver paramNameResolver;public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);if(resolvedReturnType instanceof Class) {this.returnType = (Class)resolvedReturnType;} else if(resolvedReturnType instanceof ParameterizedType) {this.returnType = (Class)((ParameterizedType)resolvedReturnType).getRawType();} else {this.returnType = method.getReturnType();}this.returnsVoid = Void.TYPE.equals(this.returnType);this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();this.returnsCursor = Cursor.class.equals(this.returnType);this.mapKey = this.getMapKey(method);this.returnsMap = this.mapKey != null;this.rowBoundsIndex = this.getUniqueParamIndex(method, RowBounds.class);this.resultHandlerIndex = this.getUniqueParamIndex(method, ResultHandler.class);this.paramNameResolver = new ParamNameResolver(configuration, method);}public Object convertArgsToSqlCommandParam(Object[] args) {return this.paramNameResolver.getNamedParams(args);}public boolean hasRowBounds() {return this.rowBoundsIndex != null;}public RowBounds extractRowBounds(Object[] args) {return this.hasRowBounds()?(RowBounds)args[this.rowBoundsIndex.intValue()]:null;}public boolean hasResultHandler() {return this.resultHandlerIndex != null;}public ResultHandler extractResultHandler(Object[] args) {return this.hasResultHandler()?(ResultHandler)args[this.resultHandlerIndex.intValue()]:null;}public String getMapKey() {return this.mapKey;}public Class<?> getReturnType() {return this.returnType;}public boolean returnsMany() {return this.returnsMany;}public boolean returnsMap() {return this.returnsMap;}public boolean returnsVoid() {return this.returnsVoid;}public boolean returnsCursor() {return this.returnsCursor;}private Integer getUniqueParamIndex(Method method, Class<?> paramType) {Integer index = null;Class[] argTypes = method.getParameterTypes();for(int i = 0; i < argTypes.length; ++i) {if(paramType.isAssignableFrom(argTypes[i])) {if(index != null) {throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");}index = Integer.valueOf(i);}}return index;}private String getMapKey(Method method) {String mapKey = null;if(Map.class.isAssignableFrom(method.getReturnType())) {MapKey mapKeyAnnotation = (MapKey)method.getAnnotation(MapKey.class);if(mapKeyAnnotation != null) {mapKey = mapKeyAnnotation.value();}}return mapKey;}}public static class SqlCommand {private final String name;private final SqlCommandType type;public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {String statementName = mapperInterface.getName() + "." + method.getName();MappedStatement ms = null;if(configuration.hasStatement(statementName)) {ms = configuration.getMappedStatement(statementName);} else if(!mapperInterface.equals(method.getDeclaringClass())) {String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();if(configuration.hasStatement(parentStatementName)) {ms = configuration.getMappedStatement(parentStatementName);}}if(ms == null) {if(method.getAnnotation(Flush.class) == null) {throw new BindingException("Invalid bound statement (not found): " + statementName);}this.name = null;this.type = SqlCommandType.FLUSH;} else {this.name = ms.getId();this.type = ms.getSqlCommandType();if(this.type == SqlCommandType.UNKNOWN) {throw new BindingException("Unknown execution method for: " + this.name);}}}public String getName() {return this.name;}public SqlCommandType getType() {return this.type;}}public static class ParamMap<V> extends HashMap<String, V> {private static final long serialVersionUID = -2212268410512043556L;public ParamMap() {}public V get(Object key) {if(!super.containsKey(key)) {throw new BindingException("Parameter \'" + key + "\' not found. Available parameters are " + this.keySet());} else {return super.get(key);}}}
}

  OK。整个动态代理一次完整的调用就结束了,可以看到最后的核心还是回到sqlsession。

2、SqlSessionFactory、SqlSession

从上面这个一大圈调用流程来看sqlsession才是关键吗,它才是封装sql操作的核心人物,那就来看看他是怎么生成的,我们调用的实现类又是那个呢?

sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);

 通常我们是这样先来获取sqlsessionfactory,它是怎么来的?

SqlSessionFacotryBuild()

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {SqlSessionFactory var5;try {XMLConfigBuilder e = new XMLConfigBuilder(reader, environment, properties);var5 = this.build(e.parse());} catch (Exception var14) {throw ExceptionFactory.wrapException("Error building SqlSession.", var14);} finally {ErrorContext.instance().reset();try {reader.close();} catch (IOException var13) {;}}return var5;}

public SqlSessionFactory build(Configuration config) {    return new DefaultSqlSessionFactory(config);}

这里看到先去解析xml配置文件,然后保存到Configuration对象里。 最后创建一个DefaultSqlSessionFactory。  有了SqlSessionFactory再来看看SqlSession。

SqlSession sqlSession=sqlSessionFactory.openSession();

DefaultSqlSessionFactory

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;DefaultSqlSession var8;try {Environment e = this.configuration.getEnvironment();TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(e);tx = transactionFactory.newTransaction(e.getDataSource(), level, autoCommit);Executor executor = this.configuration.newExecutor(tx, execType);var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);} catch (Exception var12) {this.closeTransaction(tx);throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);} finally {ErrorContext.instance().reset();}return var8;}private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {DefaultSqlSession var8;try {boolean e;try {e = connection.getAutoCommit();} catch (SQLException var13) {e = true;}Environment environment = this.configuration.getEnvironment();TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);Transaction tx = transactionFactory.newTransaction(connection);Executor executor = this.configuration.newExecutor(tx, execType);var8 = new DefaultSqlSession(this.configuration, executor, e);} catch (Exception var14) {throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var14, var14);} finally {ErrorContext.instance().reset();}return var8;}

  这里看到两个方法最后都返回了DefaultSqlSession类,也就是说最后你的工作都由DefaultSqlSession来完成。

总结

看了这两个关键类的分析,其实我们还遗漏两个比较低调的类Configuration、SimpleExecutor(实现了接口Executor)。通常我们说的SqlSession来完成我们的工作最后都要落实在执行器上,执行器是对Statement的封装。SqlSession也就是个调度的角色吧。Configuration类帮我们解析xml文件并保存其中的配置信息,在框架流转的过程中使用。

转载于:https://www.cnblogs.com/sword-successful/p/10164864.html

MyBatis动态代理执行原理相关推荐

  1. 细说Mybatis一级缓存、二级缓存以及mybatis获取mapper的面向接口编程思想(Mapper接口动态代理实现原理)(二)

    上一章和大家分享了Mybatis一级缓存和二级缓存,本章将继续和大家分享Mapper接口动态代理实现原理,按照国际惯例,先看源码,然后结合原理,写一个自己的小demo,从理论到实战,真正掌握面向接口编 ...

  2. MyBatis动态代理原理

    1. MyBatis核心组件 在详细探究MyBatis中动态代理机制之前,先来补充一下基础知识,认识一下MyBatis的核心组件. SqlSessionFactoryBuilder(构造器): 它可以 ...

  3. 超全MyBatis动态代理详解!(绝对干货)

    我的新课<C2C 电商系统微服务架构120天实战训练营>在公众号儒猿技术窝上线了,感兴趣的同学,可以长按扫描下方二维码了解课程详情: 课程大纲请参见文末 前言 假如有人问你这么几个问题,看 ...

  4. java高级----Java动态代理的原理

    Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程 ...

  5. Spring 初识Aop JDK动态代理实现 原理初显

    Spring 初识Aop JDK动态代理实现 原理初显 一.项目结构 二.具体步骤: 1.创建maven项目 创建好包结构 2.写一个TestDao接口 及实现类 3. 写一个自己的切面类 4.jav ...

  6. Mybatis动态代理模式实现CRUD

    项目实现的功能 查询所有用户信息 通过Id查询用户信息 添加用户(回显主键) 修改用户信息 删除用户信息 通过用户名字模糊查询 一.引入依赖和工程结构 <?xml version="1 ...

  7. Java动态代理的原理

    Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程 ...

  8. java 动态代理实现原理

    上篇讲了:java动态代理浅析  这篇讲讲其内部实现原理. 1.相关的类和接口 1.1 java.lang.reflect.Proxy 这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组 ...

  9. Mybatis动态代理和非动态代理的理解

    Mybatis动态代理的好处 传统分层模式,我们在处理业务的时候需要些 dao 层,然后再 写 dao层的实现类 具体处理业务在 dao 层的实现类中进行处理, 而 mybatis 在配置 实体类的  ...

  10. CGLIB 动态代理及其原理分析

    一.简介   CGLIB,即 Code Generation Library,是一个强大的.高性能的代码生成库.它可以在运行期扩展 Java 类与实现 Java 接口(JDK 动态代理只能用于接口), ...

最新文章

  1. 腾讯发现Google Home首个无接触攻破漏洞
  2. linux与虚拟化实验室,Linux·学习笔记(2)虚拟化与仿真
  3. php获取ios,IOS 通过描述获取UDID PHP代码版
  4. [HTTP] HTTP是什么
  5. StringUtil和StringUtils的区别
  6. mysql读写分离延迟问题_MySQL读写分离后的延迟解决方案
  7. 个人计算机组装主板,电脑主板安装详细图解 可以自己组装电脑了
  8. Java 数学三角函数正弦、余弦、正切以及反正弦、反余弦、反正切函数的使用
  9. 解决macOS邮件mail收取163邮件占用高CPU和下载不动的问题
  10. 全通系统定义、零极点关系、应用
  11. 计算机配置35%卡住不动了,win7配置更新35%不动怎么回事_win7配置windows update完成35卡住不动了如何解决...
  12. consul - Go服务发现、配置管理中心服务
  13. android手机分辨率,xDpi,yDpi,尺寸等各种相关物理参数
  14. 标准Lipschitz连续函数与伪Lipschitz连续函数
  15. Excel中随机生成数字,函数RANDBETWEEN()的使用
  16. 华为云-软件产品案例分析
  17. recall和precise的区别
  18. 六支团队共获第八届香港科大百万奖金创业大赛180万探索种子基金
  19. upnp 播放器 android,基于Android系统的UPNP媒体播放器的研究与实现
  20. python的or的用法_python中or和and的用法

热门文章

  1. CSDN的私信,手机与电脑发的消息,不能同时显示?
  2. 主板找不到SSD解决一例
  3. 如果有人私信,吾看到后都会及时回答
  4. 人都喜欢抬杠:一时不抬杠就浑身难受综合症候群
  5. 减少工作时间更有利于百姓
  6. SHELL下把一个文件附加到另外一个文件,注意编码问题
  7. ImportError: No module named _internal
  8. 无锡鼋头渚樱花颜色单调
  9. 管理感悟:不谈态度,只谈做法
  10. 今年中秋云遮月,来年元宵雨打灯