MyBatis中Mapper对象的创建过程

在前面的MyBatis中Mapper接口的扫描过程一文中介绍到,MyBatis将扫描到的mapper接口对应的bean定义的类型设置为了MapperFactoryBean。这是一个FactoryBean,Spring会调用它的getObject方法来创建mapper对象。本文就来分析一下这是怎样一个过程。


Mapper对象的创建过程会经过很多个类,下面一个个来分析。

MapperFactoryBean

构造方法

v3.x
java
mybatis-spring/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java
private Class<T> mapperInterface;

private boolean addToConfig = true;
public MapperFactoryBean() {
  // intentionally empty
}

public MapperFactoryBean(Class<T> mapperInterface) {
  this.mapperInterface = mapperInterface;
}

MyBatis中Mapper接口的扫描过程一文中介绍到,在处理bean定义时,会将mapper接口的类型设置为bean的构造器参数。所以实际执行的时候执行的是有参构造方法。另外也会设置addToConfig属性的值,只不过两边的默认值都是true。

另外MapperFactoryBean继承了父类SqlSessionDaoSupport

v3.x
java
mybatis-spring/src/main/java/org/mybatis/spring/support/SqlSessionDaoSupport.java
private SqlSessionTemplate sqlSessionTemplate;
// 会被setter注入,MyBatisAutoConfiguration中会注册SqlSessionFactory
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
  if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
    /*
     * 从SqlSessionFactory创建SqlSessionTemplate,但是在Spring Boot中,
     * 因为会注入一个SqlSessionTemplate(在下面的setter方法上打个断点就知道了),所以这里设置的其实无效。
     */
    this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
  }
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
  this.sqlSessionTemplate = sqlSessionTemplate;
}

注意,因为在MybatisAutoConfigurataion中会注册一个SqlSessionTemplate的方法,上面的setter方法会被调用,而且会覆盖构造方法中创建的。

v3.x
sqlSessionTemplate
SqlSessionTemplate
<
>
java
mybatis-spring-boot-2.3.2/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  ExecutorType executorType = this.properties.getExecutorType();
  /*
   * 创建SqlSessionTemplate对象,后续创建Mapper代理对象会被用到。
   */
  if (executorType != null) {
    return new SqlSessionTemplate(sqlSessionFactory, executorType);
  } else {
    return new SqlSessionTemplate(sqlSessionFactory);
  }
}
java
mybatis-spring/src/main/java/org/mybatis/spring/SqlSessionTemplate.java

private final SqlSessionFactory sqlSessionFactory;

private final ExecutorType executorType;

private final SqlSession sqlSessionProxy;

private final PersistenceExceptionTranslator exceptionTranslator;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
  this(sqlSessionFactory, executorType,
      new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator) {

  notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  notNull(executorType, "Property 'executorType' is required");

  this.sqlSessionFactory = sqlSessionFactory;
  this.executorType = executorType;
  this.exceptionTranslator = exceptionTranslator;
  // 创建SQlSession的代理对象
  this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
      new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}

创建bean对象

v3.x
getObject
getSqlSession
<
>
java
mybatis-spring/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java
@Override
public T getObject() throws Exception {
  // 通过sqlSession来创建mapper代理对象
  return getSqlSession().getMapper(this.mapperInterface);
}
java
mybatis-spring/src/main/java/org/mybatis/spring/support/SqlSessionDaoSupport.java
public SqlSession getSqlSession() {
  return this.sqlSessionTemplate;
}

SqlSessionTemplate

v3.x
java
mybatis-spring/src/main/java/org/mybatis/spring/SqlSessionTemplate.java
@Override
public <T> T getMapper(Class<T> type) {
  // 通过Configuration对象来获取Mapper对象
  return getConfiguration().getMapper(type, this);
}
@Override
public Configuration getConfiguration() {
  return this.sqlSessionFactory.getConfiguration();
}

直接调用了Configuration对象的getMapper方法。

Configuration

v3.x
java
mybatis-3/src/main/java/org/apache/ibatis/session/Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

这里又直接调用了MapperRegistrygetMapper方法。

MapperRegistry

v3.x
java
mybatis-3/src/main/java/org/apache/ibatis/binding/MapperRegistry.java
// 记录mapper文件中的namespace配置的类
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  // 获取创建mapper对象的工厂
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  // 如果没有与该类型相关的mapper配置文件
  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);
  }
}

现在只需要知道这里的knownMappers是在MyBatis解析了mapper文件中就会往里面添加元素即可。继续看创建mapper对象的逻辑。

MapperFactory

MapperFacotry类非常简单,整个类的代码都不长,该类的目的就是为了创建mapper对象。

v3.x
java
mybatis-3/src/main/java/org/apache/ibatis/binding/MapperProxyFactory.java
public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethodInvoker> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    // 通过JDK动态代理创建代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    // 创建执行处理器对象,mapper接口中的方法会被该处理器中的invoke方法拦截
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    // 对象mapper对象
    return newInstance(mapperProxy);
  }

}

重点关注上面的两个newInstance方法,可以发现,MyBatis使用了Java的动态代理功能来为接口生成实现代理类,并创建其实例,即为本文所分析的目标:Mapper对象。

这里创建代理时传入的InvocationHandlerMapperProxy类型,其invoke方法实现了增强逻辑。所以要分析mapper方法,就从该方法入手。

总结

本文分析了MyBatis中mapper对象的创建过程,最终发现了MyBatis是通过Java动态代理来为mapper接口生成代理实现类,并创建代理对象。