In-depth analysis of the running process of mybatis (2)

Then the above one continues to look down:

The execution process of SQLSession, with the SQLSessionFactory object, you can easily obtain the SQLSession. But SQLSession is also an interface.

In a previous article, we saw this code:

UserDao mapper=sqlSession.getMapper(UserDao.class);

From this code, we can see that we can get the desired object by just giving a Mapper interface; but here, what we get is the proxy object, let's see how it gets it step by step:

First, let's take a look at the getMapper method. Since SQLSession is an interface, let's go to its implementation class DefaultSqlSession to view this method:

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

Here we can see that it runs to the getMapper method of the configuration object to obtain the corresponding interface object. Continue to follow up:

I see that I have entered the class of Configuration, and take a look at the method:

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

It actually calls the getMapper method of the mapper register mapperRegistry here to get the corresponding interface object. Let's take a look at this mapperRegistry register:

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

First, use the if (mapperProxyFactory == null) method to determine whether a Mapper is registered. If not, an exception will be thrown; if there is, the mapperProxyFactory object will be called to generate a proxy mechanism; continue to follow up:

We enter the MapperProxyFactory class

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

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

Does this feel familiar? The method protected T newInstance(MapperProxy<T> mapperProxy) generates a dynamic proxy object, so mapper mapping is implemented through dynamic proxy.

Let's take a look at the proxy method:

  @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);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

This method is placed in the MapperProxy class. It first determines whether Mapper is a class, and it must be an interface here; note that the proxy here is a jdk dynamic proxy; if you ask why you say this, let's take a look this code:

public class MapperProxy<T> implements InvocationHandler, Serializable

What did you see? Yes, it is InvocationHandler. Now you know why he is a jdk dynamic proxy! Since Mapper is an interface, the judgment is false and it will go

  final MapperMethod mapperMethod = cachedMapperMethod(method);

Generate a MapperMethod object, and finally execute its execute method, and pass only the SQLSession and the current running parameters. Since there are many methods, the unimportant points will not be intercepted.

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 if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }

The executeForMap(sqlSession, args); method called here, let's take a look at this method:

  private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
    Map<K, V> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
    } else {
      result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
    }
    return result;
  }

At this point, we will not continue to follow up. We can stay at the implementation of the executeForMap method, which runs through the command mode; through the source code, we can know that it finally runs the sql of the object through the SQLSession object, and other additions, deletions and changes This is an operation similar to this;

Here we should understand that the mapper's xml configuration file namespace corresponds to the full class name of the interface, and the method is the id of the sql, so that mybatis can bind it to the proxy according to the full class name and method name. Together;

In this section, we saw the general workflow of SQLSession, which is to initialize the sql of the configuration file, pass parameters to it, execute the sql statement and get the return value;

So how it is implemented, the next article will elaborate;


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325592168&siteId=291194637