mybatis源码分析 ----- Mybatis为什么只写接口就能执行sql?

Mybatis为什么只写接口就能执行sql?

至于这个问题,也算是一个常见的面试题,基本都会答动态代理,那么动态代理到底是如何做的呢?

Configuration对象

Mybatis会将配置文件和映射文件中的所有信息保存在这个对象中。其中有一个属性为mapperRegistry。这个属性保存接口和它对应的代理工厂

 // 接口和代理类的注册中心
  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

MapperRegistry这个类中含有一个属性名为knowMappers,本质上是一个hashMap,其中key为Class类型,value为MapperProxyFactory类型。

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

其中这个class就是我们所写的接口的class对象,当我们执行getMapper(mapper.class)的时候,会执行如下逻辑:

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    
    
    // 获取代理类工厂
    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);
    }
  }

使用map的get方法,获取到对应的代理工厂,然后使用代理工厂创建代理对象。最终代码来到这:

 protected T newInstance(MapperProxy<T> mapperProxy) {
    
    
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {
    
     mapperInterface }, mapperProxy);
  }

其中最后一个参数mapperProxy是之前创建的一个对象,这个类实现了InvocationHandler接口,因此最终我们会执行这个类中的invoke方法。

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
    try {
    
    
    // Object类中的对象不需要增强
      if (Object.class.equals(method.getDeclaringClass())) {
    
    
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
    
    
      // jdk8中接口中的默认反复不需要增强
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
    
    
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 主要逻辑
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

其中mapperMathod相当于一个桥梁,一方面沟通接口中的方法和参数,另一方面沟通mapper.xml文件中的sql语句。最终调用execute方法。
接下来就是sql执行的逻辑了。

猜你喜欢

转载自blog.csdn.net/weixin_43213517/article/details/107269324