承接前文Spring mybatis源码篇章-MapperScannerConfigurer

前话

根据前文的分析我们可以得知Spring在使用MapperScannerConfigurer扫描DAO接口类集合时,会将相应的DAO接口封装成类型为org.mybatis.spring.mapper.MapperFactoryBean对象,并将相应的mapperInterface(dao接口)加入至mybatis框架中的org.apache.ibatis.session.Configuration对象里

configuration.addMapper(this.mapperInterface);

笔者深究下这个方法的调用,跟踪下去发现其实其调用的是org.apache.ibatis.binding.MapperRegistry#addMapper()方法。本文则通过这个方法进行展开讲解

MapperRegistry#addMapper()

直接查看相应的源码

  public <T> void addMapper(Class<T> type) {
// 类必须为接口类
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 放入knownMappers中,并以MapperProxyFactory来进行包装
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}

由上述的代码可知,所加入的mapperInterface正如其英文描述一样,必须是一个接口类。而且mybatis还将此接口类包装成org.apache.ibatis.binding.MapperProxyFactory对象,很有代理的味道~~~

MapperProxyFactory

上文讲了如何存放,那么获取的代码呢??如下所示

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 从map中获取相应的代理类
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);
}
}

我们直接去翻看下MapperProxyFactory#newInstance()方法

  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);
}

粗粗一看,发现是最终是通过JDK动态代理来代理相应的mapper接口。而对应的代理处理则为java.lang.reflect.InvocationHandler接口的实现类org.apache.ibatis.binding.MapperProxy。接下来笔者针对这个类进行详细的分析

MapperProxy

首先看下构造函数

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
// sql会话
this.sqlSession = sqlSession;
// mapper接口
this.mapperInterface = mapperInterface;
// 对应接口方法的缓存
this.methodCache = methodCache;
}

我们直接看下代理的实现方法invoke()

  @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
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);
}
// 关注此处即可
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}

看来真正处理的是org.apache.ibatis.binding.MapperMethod类,我们继续分析

MapperMethod

也观察下构造函数

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// SqlCommand表示该sql的类型,一般为select|update|insert|delete|flush等类型
this.command = new SqlCommand(config, mapperInterface, method);
// method适配器,一般解析mapper接口对应method的参数集合以及回参等
this.method = new MethodSignature(config, mapperInterface, method);
}

继而简单的看下其execute()方法

  public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 根据command的类型进行CRUD操作
switch (command.getType()) {
case INSERT: {
// 解析入参集合,@Param注解。详见ParamNameResolver#names注解
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
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() + ").");
}
return result;
}

其实很简单,就是最终还是通过Sqlsession来进行真正的sql执行。我们可以简单看下sqlsession接口的方法

public interface SqlSession extends Closeable {

  <T> T selectOne(String statement);

  <T> T selectOne(String statement, Object parameter);

  <E> List<E> selectList(String statement);

  <E> List<E> selectList(String statement, Object parameter);

  <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);

  <K, V> Map<K, V> selectMap(String statement, String mapKey);

  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);

  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

  <T> Cursor<T> selectCursor(String statement);

  <T> Cursor<T> selectCursor(String statement, Object parameter);

  <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);

  void select(String statement, Object parameter, ResultHandler handler);

  void select(String statement, ResultHandler handler);

  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);

  int insert(String statement);
nsert(String statement, Object parameter); int update(String statement); int update(String statement, Object parameter); int delete(String statement); int delete(String statement, Object parameter); void commit(); void commit(boolean force); void rollback(); void rollback(boolean force); List<BatchResult> flushStatements(); void close(); void clearCache(); Configuration getConfiguration(); <T> T getMapper(Class<T> type); Connection getConnection();
}

提供数据库操作的CRUD方法~~很齐全

小结

作下简单的小结

1.mybatis对mapper接口的对应方法采取了JDK动态代理的方式

2.SERVICE或者CONTROLLER层调用mapper接口的时候,便会通过mapperRegistry去获取对应的mapperMethod来进行相应的SQL语句操作

Mybatis源码解析-MapperRegistry注册mapper接口的更多相关文章

  1. mybatis源码解析8---执行mapper接口方法到执行mapper.xml的sql的过程

    上一篇文章分析到mapper.xml中的sql标签对应的MappedStatement是如何初始化的,而之前也分析了Mapper接口是如何被加载的,那么问题来了,这两个是分别加载的到Configura ...

  2. Mybatis源码解析-MapperRegistry代理注册mapper接口

    知识储备 SqlsessionFactory-mybatis持久层操作数据的前提,具体的解析是通过SqlSessionFactoryBean生成的,可见>>>Spring mybat ...

  3. MyBatis源码解析【7】接口式编程

    前言 这个分类比较连续,如果这里看不懂,或者第一次看,请回顾之前的博客 http://www.cnblogs.com/linkstar/category/1027239.html 修改例子 在我们实际 ...

  4. Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析

    在上一篇文章Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例中我们谈到了properties,settings,envir ...

  5. Mybatis源码解析(三) —— Mapper代理类的生成

    Mybatis源码解析(三) -- Mapper代理类的生成   在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...

  6. mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)

    目录 1. 简介 2. 解析 3 StrictMap 3.1 区别HashMap:键必须为String 3.2 区别HashMap:多了成员变量 name 3.3 区别HashMap:key 的处理多 ...

  7. mybatis源码-解析配置文件(四)之配置文件Mapper解析

    在 mybatis源码-解析配置文件(三)之配置文件Configuration解析 中, 讲解了 Configuration 是如何解析的. 其中, mappers作为configuration节点的 ...

  8. Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取

    在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao ...

  9. 【MyBatis源码解析】MyBatis一二级缓存

    MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...

随机推荐

  1. 使用Azure Policy(策略)强制实现资源Tag的坑

    Azure的Tag(标记)可以帮助运维人员对云资源分类从而方便地进行计费和资源管理.然而在具体实践中工程师部署云资源的时候常常会忘记给资源做标记打Tag. 针对这个问题,Azure的官方文档建议是可以 ...

  2. django favicon配置

    其实网站加一个图标,在/static/images/里面放置favicon.ico 1. 直接url里修改 from django.views.generic.base import Redirect ...

  3. SpringMVC中使用Swagger2整合

    Swagger2是什么 Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件. Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 W ...

  4. jq瀑布流代码

    <style> #zh{ position:fixed; width:100%; height:100%; background:url(images/bgblack.png); top: ...

  5. arcgis api for js入门开发系列十一地图统计图

    上一篇实现了demo的叠加SHP图层,本篇新增地图统计图,截图如下: 地图统计图实现的思路如下:利用拓展arcgis api的js文件(MapChartGraphic.js以及MapChartGrap ...

  6. css3自适应圆

    .class{ width:auto; height:auto; border-radius:11px; min-width:14px; padding:0 4px; font-size:12px; ...

  7. ELK-初识Elasticsearch

    第一篇:初识Elasticsearch 1.安装 Elasticsearch 要求 java8+的环境,推荐使用 Oracle 1.8.0_131版本的JDK.Java JDK的安装此处不做介绍.这里 ...

  8. 【jframe】Java Web应用程序框架 - 第01篇:Get Started

    jframe是什么? jframe是一个基于MIT协议开源的java web应用程序框架,汇聚了我们团队之于java web应用程序的核心架构思想以及大量最佳实践,并且持续在实际项目中不断完善优化. ...

  9. 【LeetCode】220. Contains Duplicate III

    题目: Given an array of integers, find out whether there are two distinct indices i and j in the array ...

  10. java--while、do while、for三种循环体

    1.for可以记录执行次数: 2.while.do while的i放在sum的后面和for得到的执行次数和结果是一致的. 1.从执行结果来看,放在前面,虽然执行次数和i放在sum的后面是相同,但是结果 ...