Mybatis为何接口不做实现也能执行sql?

//创建sqlsession以后获取mapper调用方法执行sql
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            List<Employee> all = employeeMapper.getAll();

但是上面的Mapper接口并没有实现类,那么为何最终能获取到结果呢,其实挺明显就是动态代理,接下来debug看代理类的生成和调用过程

DefaultSqlSession.class

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

Configuration.class

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

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  //检查是否扫描到了该接口,所以要注意<mapper namespace="mapper.EmployeeMapper">中内容,特别注意package名也加上
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //最终是代理工厂创建mapper
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
  
  //创建代理类
    public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

从名字也可以看出来,MapperProxy就是mapper接口的代理类,点进去果不其然

//JDK动态代理
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  //缓存MapperMethod,这是封装sql操作的类
  private final Map<Method, MapperMethod> methodCache;

...

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //Object类的方法直接调
    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);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      //如果还没调用过,生成MapperMethod并且缓存
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }
  
  接下来就是command执行过程
MapperMethod.class
   public Object execute(SqlSession sqlSession, Object[] args) {

DefaultSqlSession
    selectOne/selectMap...对应方法
Executor
     ...对应方法

总结:
mybatis先扫描配置类和mapper接口文件,生成保存配置和mapper信息的Configuration和MapperRegistry,在实际调用中利用jdk动态代理把执行交给 SqlSession对应方法。

原文地址:https://www.cnblogs.com/CodeSpike/p/13729958.html