MyBatis源码解析4-每个Mapper类都会生成一个唯一的Mapper代理对象

mybatis-spring-1.3.2.jar 中有5个类

org.mybatis.spring.mapper.MapperScannerConfigurer
org.mybatis.spring.mapper.ClassPathMapperScanner
org.mybatis.spring.mapper.MapperFactoryBean

org.mybatis.spring.SqlSessionTemplate
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor

Mapper代理对象注册到Spring容器

我们看下MapperScannerConfigurer的定义

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

  private String basePackage;

  private boolean addToConfig = true;

  private SqlSessionFactory sqlSessionFactory;

  private SqlSessionTemplate sqlSessionTemplate;

  private String sqlSessionFactoryBeanName;

  private String sqlSessionTemplateBeanName;

  private Class<? extends Annotation> annotationClass;

  private Class<?> markerInterface;

  private ApplicationContext applicationContext;

  private String beanName;

  private boolean processPropertyPlaceHolders;

  private BeanNameGenerator nameGenerator;

	// ...省略了若干方法的定义
	//...
  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
}

实现了BeanDefinitionRegistryPostProcessor接口,说明这个类在Spring容器初始化的过程中会被执行,在bean的定义被注册后,会进入这个执行过程。
在方法postProcessBeanDefinitionRegistry 创建了一个ClassPathMapperScanner 。 而ClassPathMapperScanner会为每个用户定义的Mapper interface注册一个Bean,而且这个Bean是从MapperFactoryBean#getObject来的。
虽然MapperFactoryBean本身是单例模式的(inSingleton() return true). 假设用户定义来两个Mapper interface。 比如StudentMapper和SecondStudentMapper两个interface。
则MapperScannerConfigurer会添加两个BeanDefinition,每个对应一个MapperFactoryBean.
而MapperFactoryBean实现了FactoryBean接口,所以会分别new一个MapperFactoryBean,
并通过MapperFactoryBean.geObject() 返回一个对象。比如StudentMapper对象和SecondStudentMapper对象。

MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean

创建的过程中,会设置SqlSessionDaoSupport中的一些字段。比如(SqlSessionTemplate等)
MapperFactoryBean.getObject()

  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
// -->   SqlSessionTemplate.java

@Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }
//调用链
SqlSessionTemplate#getMapper(Class<T> type))
Configuration#mapperRegistry.getMapper(type, sqlSession);
MapperRegistry#getMapper(Class<T> type, SqlSession sqlSession)
MapperProxyFactory#newInstance()
产生一些代理类的对象。 类型为StudentMapper。 
对应的InvocationHandler是MapperProxy.java
Proxy.

至此StudentMapper的代理对象就已经生成了。并以单例到模式注册到spirng容器

用户调用Mapper的方法

这个实现由下面两个类来完成

org.mybatis.spring.SqlSessionTemplate
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor

还是使用的JDK动态代理,这个就很简单。直接贴关键代码

public class SqlSessionTemplate implements SqlSession, DisposableBean {
  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;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }
private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

sqlSessionProxy是 SqlSessionInterceptor关联的一个动态代理对象.
sqlSessionProxy的增删改查,都会通过代理对象,这样都会进入SqlSessionInterceptor#invoke

  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.sqlSessionProxy.<E> selectList(statement, parameter);
  }

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
		...
}

sqlSession会真正根据sql语句去操作数据库并返回结果。

SqlSession.java

package org.apache.ibatis.session;

import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;

public interface SqlSession extends Closeable {
    <T> T selectOne(String var1);

    <T> T selectOne(String var1, Object var2);

    <E> List<E> selectList(String var1);

    <E> List<E> selectList(String var1, Object var2);

    <E> List<E> selectList(String var1, Object var2, RowBounds var3);

    <K, V> Map<K, V> selectMap(String var1, String var2);
    ...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值