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);
    ...
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章