MyBatis Mapper in principle to use interface

MyBatis Mapper in principle to use interface

MyBatis 3 Mapper interface recommended way to perform xml configuration of SQL, it is easy to use, and very flexible. In the remainder of convenience, I would like to know how this is achieved, before also generally know by the JDK dynamic proxy to do, but this time wanted to know the details.

The more things more complex, so with a simple rely only MyBatis CRUD 3.4.0 to develop an understanding of call Mapper interface.

Typically xml configuration file created by SqlSessionFactorythe object, and then obtain SqlSessionthe object, and then obtain an interface proxy object Mapper custom last method call interface, the following example:

/**
 *
 * @author xi
 * @date 2018/10/01 14:12
 */
public class Demo {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory =
                new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
        Role role = roleMapper.getRole(1L);
        System.out.println(role);
    }
}

How to parse the configuration file, create a factory, get this session are not the focus, you can look directly at the following line:

RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);

Custom acquisition method Mapper proxy object is located at: org.apache.ibatis.session.SqlSession#getMapper, or a generic method

  /**
   * Retrieves a mapper.
   * @param <T> the mapper type
   * @param type Mapper interface class
   * @return a mapper bound to this SqlSession
   */
  <T> T getMapper(Class<T> type);

Subclasses implement this method are: DefaultSqlSessionand SqlSessionManager, where attention to the default implementation:org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper

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

There appeared Configuration object, the object is simply contains the xml configuration parsing content, it is not now the same focus, continue to follow up down:org.apache.ibatis.session.Configuration#getMapper

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

Here appeared MapperRegistryobjects, it parses the content of Mapper.xml ( mappertag namespacecontains the fully qualified name of the interface Mapper) come, HashMap comprising a member variable org.apache.ibatis.binding.MapperRegistry#knownMappers, key is Mapper interface Classobjects, value is org.apache.ibatis.binding.MapperProxyFactory, can be seen from the name is used to create a proxy object factory Mapper interface will be used later.

In particular this knownMappersis how to fill, as detailed org.apache.ibatis.binding.MapperRegistry#addMappermethod, regardless of being first to go down:org.apache.ibatis.binding.MapperRegistry#getMapper

  @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);
    }
  }

Depending on the type Mapper interface, from the knownMappersget corresponding plant, and then create a proxy object, continue to follow up:org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)

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

Here again there is a MapperProxytarget, it should be understood that a proxy object, opened it implements the java.lang.reflect.InvocationHandlerinterface, which is trying to sell wow.
Do not look at the dog, continue to follow up:org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.binding.MapperProxy<T>)

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

In this, I see the familiar java.lang.reflect.Proxy, here mapperInterfaceis passed when creating the factory Mapper interface. The real proxy object Mapper interface this time to produce, is really a sheep's head.

This is not both familiar and unfamiliar JDK dynamic proxy What, are familiar with it, because in front of the dog mapperProxyis an InvocationHandlerobject that intercepts all calls to the proxy object interface methods. It is strange because the use of concrete subclasses will implement the interface when JDK dynamic proxies before, did not see here. Then take a look at org.apache.ibatis.binding.MapperProxy:

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 判断 method 是不是 Object 类的方法,如 hashCode()、toString()
    if (Object.class.equals(method.getDeclaringClass())) {
      try {// 如果是,则调用当前 MapperProxy 对象的这些方法
      // 跟 Mapper 接口的代理对象没关系
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    // 到这了,说明调用的是接口中的方法,具体的执行就不是本次关注的重点了
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

// 对 MapperMethod 做了缓存,这个 methodCache 是个 ConcurrentHashMap,在 MapperProxyFactory 中创建的
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

Specific instructions are inside code comments, nothing to say.

to sum up

  1. By JDK dynamic proxy mode, create a proxy object Mapper interface, intercept calls to interface methods;
  2. Mapper interfaces can not be used in heavy-duty, see specific reasons org.apache.ibatis.binding.MapperMethod.SqlCommand#SqlCommand, may depend on it org.apache.ibatis.session.Configuration#mappedStatements.

Finally, the last, good old saying: failing to decide, firstOpen big dragon(See source code) (escape

Guess you like

Origin www.cnblogs.com/magexi/p/11756515.html