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

知识储备

  1. SqlsessionFactory-mybatis持久层操作数据的前提,具体的解析是通过SqlSessionFactoryBean生成的,可见>>>Spring mybatis源码篇章-SqlSessionFactoryBean

  2. MapperInterface-mybatis的java接口类,用于service/controller层的调用,具体的解析可见>>>Spring mybatis源码篇章-MapperScannerConfigurer关联dao接口

  3. 保存mybatis的各种信息都是由org.apache.ibatis.session.Configuration类来维护的

入口

入口由Configuration#addMapper()方法调用,如下述MapperFactoryBean#checkConfig()源码

  @Override
  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        //只关注此处的添加mapper接口
        configuration.addMapper(this.mapperInterface);
      } catch (Throwable t) {
        throw new IllegalArgumentException(t);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

具体的调用哪个类进行代理的生成,我们继续往下查看

MapperRegistry#addMapper()

通过MapperRegistry来创建mapper访问代理,直接阅读源码

  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 {
        //将mapper接口包装成mapper代理
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        //解析接口上的注解或者加载mapper配置文件生成mappedStatement
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

此处笔者只关注MapperProxyFactory类,其是生成mapper代理的关键

MapperProxyFactory
  • 内部属性概览
  //被代理类
  private final Class<T> mapperInterface;
  //支持对被代理类进行缓存
  private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
  • 创建代理类
      @SuppressWarnings("unchecked")
      protected T newInstance(MapperProxy<T> mapperProxy) {
        //采用JDK自带的Proxy代理模式生成
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }

      public T newInstance(SqlSession sqlSession) {
        //MapperProxy为InvocationHandler的实现类
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        //真实生成代理
        return newInstance(mapperProxy);
      }

接下来阅读下MapperProxy是怎么运用反射调用mapper接口类的方法

MapperProxy

由于要使用JDK的动态代理,直接InvokeHandler接口的实现方法

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
          try {
            return method.invoke(this, args);
          } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
        //尝试从缓存中获取,也就是看到的methodCache
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        //通过MapperMethod对象调用方法
        return mapperMethod.execute(sqlSession, args);
      }
  
      private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
          //注意此处的传的参数为mapper接口类、method对象、Configuration对象
          mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
          methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
      }

由上文我们找到了代理的关键处理类MapperMethod,笔者继续往下一窥究竟

MapperMethod
  • 构造函数
      //Sql指令类
      private final SqlCommand command;
      //方法签名类
      private final MethodSignature method;

      public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, method);
      }
  • 代理dao接口对应出口方法execute()
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //CURD操作,对持久层返回的结果集进行处理
    if (SqlCommandType.INSERT == command.getType()) {
      //获取method方法上的带有@Param的参数,默认返回0,1,2,3...
      Object param = method.convertArgsToSqlCommandParam(args);
      //最终是通过sqlSession接口对象获取结果集,注意此处的command.getName()为mappedStatementId
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (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);
      }
    } else {
      throw new BindingException("Unknown execution method for: " + command.getName());
    }
    return result;
  }
  1. SqlCommand是MapperMethod的静态内部类,主要通过mapperInterface和method从Configuration中获取MappedStatement对象保存其id和type属性,供sqlsession持久层接口调用

  2. MethodSignature是MappedMethod的静态内部类,主要对代理的method作下反射处理,包括入参、出参等操作

  3. MapperMethod的作用是处理SqlSession接口调用CRUD操作后产生的结果集

MapperRegistry#getMapper()

相对应的获取mapper代理类,源码如下

  @SuppressWarnings("unchecked")
  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 {
      //获取mapper代理类
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

那么获取代理类这个操作是如何被调用的呢?这其实是bean工厂对其里面所有的beanDefinition进行实例化调用的,这点可查看>>>Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization
而针对FactoryBean接口的实例化会调用其中的getObject()方法,所以我们看下MapperFactoryBean#getObject()方法

  public T getObject() throws Exception {
    //通过sqlSessionTemplate调用Configuration#getMapper()方法间接调用MapperRegistry#getMapper()
    return getSqlSession().getMapper(this.mapperInterface);
  }

小结

本文是对MapperFactoryBean#checkDaoConfig()方法的补充,通过对Mybatis的MapperRegistry的分析我们可以得出以下结论:

  • mapperInterface的内部方法数据持久层访问是通过JDK的代理来完成的

  • MapperMethod是对上述代理的method方法的真实处理,主要是对sqlSession的返回结果集进行对应的整理输出,具体读者可自行查阅分析

转载于:https://www.cnblogs.com/question-sky/p/7251399.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值