MyBatis接口编程原理

解析下面这两行代码,暂且不考虑和spring结合:

// 通过sqlSession执行SQL语句
IMessage imessage = sqlSession.getMapper(IMessage.class);
messageList = imessage.queryMessageList(parameter);
第一步:为接口产生一个代理类

sqlSession 实现类 DefaultSqlSession

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> 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);
        }
    }
}

那么knownMappers又是在哪初始化的呢?

//加载xml配置文件的时候会调用这个方法
public <T> void addMapper(Class<T> type) {
    ...省略...
        try {
           //关键put方法
            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);
            }

        }
    }

}

MapperProxyFactory.java的源码
上面getMapper方法里面调用的 mapperProxyFactory.newInstance(sqlSession);
实际上就是生成一个接口的动态代理类

public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
}
...省略...
 protected T newInstance(MapperProxy<T> mapperProxy) {
    return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
    MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}
}
第二步:调用代理方法

执行接口的代理类的方法 == invokeHandler的invoke方法
这似乎动态代理的知识

 public class MapperProxy<T> implements InvocationHandler
//估计加载配置的时候,把这个类初始化好
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//因为传进去的是接口,不是object,实体类的父类应该是object,不会执行
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;
  }

下面看看初始化MapperMethod的代码

   public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, method);

}

初始化了一个SqlCommond

public static class SqlCommand {

//这两个属性至关重要,后面执行,要用到这两个属性
private final String name;
private final SqlCommandType type;

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {
 //把拼接好的namespace.id 拿到配置文件里面去查找
  String statementName = mapperInterface.getName() + "." + method.getName();
  MappedStatement ms = null;
  if (configuration.hasStatement(statementName)) {
    //查询结果放在MappedStatement  这个类很关键
    ms = configuration.getMappedStatement(statementName);
  } else if  ...省略...
  name = ms.getId();
  type = ms.getSqlCommandType();
  ...省略...

既然都查到关键信息
name == namespace.id)
SqlCommandType == 执行类型
下一步就开始执行呗

public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
...省略...
(SqlCommandType.SELECT == command.getType()) {
  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 {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = sqlSession.selectOne(command.getName(), param);
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,759评论 18 399
  • 1 引言# 本文主要讲解JDBC怎么演变到Mybatis的渐变过程,重点讲解了为什么要将JDBC封装成Mybait...
    七寸知架构阅读 76,585评论 36 979
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,571评论 0 4
  • 前面的章节主要讲mybatis如何解析配置文件,这些都是一次性的初始化过程。从本章开始讲解动态的过程,它们跟应用程...
    七寸知架构阅读 4,967评论 2 55