zoukankan      html  css  js  c++  java
  • mybatis源码解析---执行mapper接口方法到执行mapper.xml的sql的过程

    <T> T getMapper(Class<T> type);

    很显然这个方法是更加Class名获取该类的一个实例,而Mapper接口只定义了接口没有实现类,那么猜想可知返回的应该就是更加mapper.xml生成的实例了。具体是如何实现的呢, 先看下这个方法是如何实现的?

    DefaultSqlSession实现该方法的代码如下:

    1 @Override
    2   public <T> T getMapper(Class<T> type) {
    3     return configuration.<T>getMapper(type, this);
    4   }

    方法很简单,调用了Configuration对象的getMapper方法,那么接下来再看下Configuration里面是如何实现的。代码如下:

    1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    2     return mapperRegistry.getMapper(type, sqlSession);
    3   }

    调用了mapperRegistry的getMapper方法,参数分别是Class对象和sqlSession,再继续看MapperRegistry的实现,代码如下:

    @SuppressWarnings("unchecked")
        public <T> T getMapper(Class<T> type, SqlSession sqlSession)
        {
            // 从konwMappers获取MapperProxyFactory对象
            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);
            }
        }

    可以看出是根据type从knowMappers集合中获取该mapper的代理工厂类,如何通过该代理工厂新建一个实例。再看下代理工厂是如何创建实例的,代码如下:

    @SuppressWarnings("unchecked")
        protected T newInstance(MapperProxy<T> mapperProxy) {
            //通过代理获取mapper接口的新实例
          return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
        }
    
        public T newInstance(SqlSession sqlSession) {
          //创建mapper代理对象,调用newInstance方法
          final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
          return newInstance(mapperProxy);
        }

    那么现在我们就知道是如何根据Mapper.class来获取Mapper接口的实例的了,不过,到目前为止貌似还是没有看到和MappedStatement产生联系啊,别急,再往下看通过代理产生的mapper实例执行具体的方法是如何进行的。代码如下:

    @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          //判断执行的方法是否是来自父类Object类的方法,也就是如toString、hashCode等方法
          //如果是则直接通过反射执行该方法,如果不是Object的方法则再往下走,如果不加这个判断会发生什么呢?
          //由于mapper接口除了定义的接口方法还包括继承于Object的方法,如果不加判断则会继续往下走,而下面的执行过程是从mapper.xml寻找对应的实现方法,
          //由于mapper.xml只实现了mapper中的接口方法,而没有toString和hashCode方法,从而就会导致这些方法无法被实现。
          if (Object.class.equals(method.getDeclaringClass())) {
            try {
              return method.invoke(this, args);
            } catch (Throwable t) {
              throw ExceptionUtil.unwrapThrowable(t);
            }
          }
          final MapperMethod mapperMethod = cachedMapperMethod(method);
          //执行mapperMethod对象的execute方法
          return mapperMethod.execute(sqlSession, args);
        }
    
        private MapperMethod cachedMapperMethod(Method method) {
          //从缓存中根据method对象获取MapperMethod对象
          MapperMethod mapperMethod = methodCache.get(method);
          if (mapperMethod == null) {
            //如果mapperMethod为空则新建MapperMethod方法
            mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
            methodCache.put(method, mapperMethod);
          }
          return mapperMethod;
        }

    可以看出代理执行mapper接口的方法会先创建一个MapperMethod对象,然后执行execute方法,代码如下:

    public Object execute(SqlSession sqlSession, Object[] args) {
            Object result;
            if (SqlCommandType.INSERT == command.getType()) {//如果执行insert命令
              Object param = method.convertArgsToSqlCommandParam(args);//构建参数
              result = rowCountResult(sqlSession.insert(command.getName(), param));//调用sqlSession的insert方法
            } else if (SqlCommandType.UPDATE == command.getType()) {//如果执行update命令
              Object param = method.convertArgsToSqlCommandParam(args);//构建参数
              result = rowCountResult(sqlSession.update(command.getName(), param));//调用sqlSession的update方法
            } else if (SqlCommandType.DELETE == command.getType()) {//如果执行delete命令
              Object param = method.convertArgsToSqlCommandParam(args);//构建参数
              result = rowCountResult(sqlSession.delete(command.getName(), param));//调用sqlSession的delete方法
            } else if (SqlCommandType.SELECT == command.getType()) {//如果执行select命令
              if (method.returnsVoid() && method.hasResultHandler()) {//判断接口返回类型,更加返回数据类型执行对应的select语句
                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);
              }
            } else if (SqlCommandType.FLUSH == command.getType()) {
                result = sqlSession.flushStatements();
            } else {
              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;
          }

    可以看出大致的执行过程就是更加MapperMethod的方法类型,然后构建对应的参数,然后执行sqlSession的方法。到现在还是没有MappedStatement的影子,再看看MapperMethod是被创建的。

    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, mapperInterface, method);
      }


     这里涉及到了两个类SqlCommand和MethodSignature,先从SqlCommand看起。

    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
            String statementName = mapperInterface.getName() + "." + method.getName();//接口方法名
            MappedStatement ms = null;
            if (configuration.hasStatement(statementName)) {
                //从configuration中根据接口名获取MappedStatement对象
              ms = configuration.getMappedStatement(statementName);
            } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35
                //如果该方法不是该mapper接口的方法,则从mapper的父类中找寻该接口对应的MappedStatement对象
              String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
              if (configuration.hasStatement(parentStatementName)) {
                ms = configuration.getMappedStatement(parentStatementName);
              }
            }
            if (ms == null) {
              if(method.getAnnotation(Flush.class) != null){
                name = null;
                type = SqlCommandType.FLUSH;
              } else {
                throw new BindingException("Invalid bound statement (not found): " + statementName);
              }
            } else {
              name = ms.getId();//设置name为MappedStatement的id,而id的值就是xml中对应的sql语句
              type = ms.getSqlCommandType();//设置type为MappedStatement的sql类型
              if (type == SqlCommandType.UNKNOWN) {
                throw new BindingException("Unknown execution method for: " + name);
              }
            }
          }

    这里终于是看到了MappedStatement的身影,根据mapper的Class对象和method对象从Configuration对象中获取指定的MappedStatement对象,然后根据MappedStatement对象的值初始化SqlCommand对象的属性。而MethodSignature则是sql语句的签名,主要作用就是对sql参数与返回结果类型的判断。

    总结:

    1、sqlSession调用configuration对象的getMapper方法,configuration调用mapperRegistry的getMapper方法

    2、mapperRegistry根据mapper获取对应的Mapper代理工厂

    3、通过mapper代理工厂创建mapper的代理

    4、执行mapper方法时,通过代理调用,创建该mapper方法的MapperMethod对象

    5、MapperMethod对象的创建是通过从configuration对象中获取指定的MappedStatement对象来获取具体的sql语句以及参数和返回结果类型

    6、调用sqlSession对应的insert、update、delete和select方法执行mapper的方法

    到目前为止知道了mapper接口和mapper.xml是如何进行关联的了,也知道mapper接口是如何获取实例的了,也知道了mapper方法最终会调用SqlSession的方法,那么SqlSession又是如何具体去执行每个Sql方法的呢?下一篇继续分析......

    没有停止的脚步,只有倒下去的脚步
  • 相关阅读:
    Encrypted Handshake Message
    RSAParameters Struct
    What if JWT is stolen?
    What's the difference between JWTs and Bearer Token?
    RSA Algorithm Example
    第18届Jolt大奖结果公布
    Ruby on rails开发从头来(windows)(三十六) 调试技巧
    Ruby on rails开发从头来(四十二) ActiveRecord基础(主键和ID)
    YouTube开放基础技术架构 让用户建自家YouTube
    Ruby on rails开发从头来(四十) ActiveRecord基础(Boolean属性)
  • 原文地址:https://www.cnblogs.com/hkMblogs/p/13174148.html
Copyright © 2011-2022 走看看