mybaits-spring 源码阅读随笔

Posted jerryqtqcjl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybaits-spring 源码阅读随笔相关的知识,希望对你有一定的参考价值。

mybaits-spring 源码阅读随笔(一)

1、@MapperScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
    ...
}

@MapperScan注解中通过@Import注解导入了MapperScannerRegistrar.class,源码如下:

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  ...
      
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
    }
  }

  /**
   * 将MapperScannerConfigurer的bean信息注册到spring容器中
   */
  void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {

    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    ...
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }

}

熟悉spring源码的应该知道,MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar,是spring的一个扩展点,我们可以通过实现这个接口动态的注册bean。(详细请看spring源码),这里mybaits注册了MapperScannerConfigurer这个类的bean信息。

private void loadBeanDefinitionsForConfigurationClass(
            ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    ...                    
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }
 

2、MapperScannerRegistrar 和 ClassPathMapperScanner

接下来,我们看一下MapperScannerRegistrar这个类,源码如下:

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
   ...
  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }
    //创建mapper扫描器
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    ...
    scanner.registerFilters();
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
   ...
}

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor,这是spring的另外一个扩展点,了解bean生命周期的童鞋应该就知道BeanDefinitionRegistryPostProcessor,是在bean实例化前最先执行的扩展点,这里创建了一个ClassPathMapperScanner,我们看一下里面具体做了些什么。

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
    ...
  private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;

  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
      //这里扫描出来的是MapperScan配置路径下所有的接口
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
         ...
    } else {
      processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
  }

    //给mapper的bean信息设置默认属性
  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
         ...
      definition.setBeanClass(this.mapperFactoryBeanClass);
        ...
      if (!explicitFactoryUsed) {   
          //这里非常重要,这里自动注入设置为byType,只要bean中有该属性的set方法,spring就会自动注入(调用set方法,及即使没有属性,set方法也会执行)
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }
}

ClassPathMapperScanner实现了ClassPathBeanDefinitionScanner(spring注解扫描器),主要是将MapperScan配置路径下所有的接口(mapper)扫描出来并将mapper的bean信息注册到beanFactory,这里注意,beanDefinition中设置的类型是MapperFactoryBean(实现了FactoryBean,这里就不多说FactoryBean了,详细可以自行谷歌或百度)。那么接下来我们就来研究一下MapperFactoryBean。

3、MapperFactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  private Class<T> mapperInterface;
  private boolean addToConfig = true;
    ...
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
}

MapperFactoryBean继承了SqlSessionDaoSupport,我们主要看一下SqlSessionDaoSupport。

public abstract class SqlSessionDaoSupport extends DaoSupport {

  private SqlSessionTemplate sqlSessionTemplate;

    //这里就是之前说到的自动注入,这里并没有SqlSessionFactory属性,但是就是会调用这个方法
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
      this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    }
  }

  @SuppressWarnings("WeakerAccess")
  protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionTemplate(sqlSessionFactory);
  }
}

我们可以看到创建了一个SqlSessionTempalte,SqlSessionTempalte实现了SqlSession接口,我们看下源码:

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  private final SqlSessionFactory sqlSessionFactory;
  private final ExecutorType executorType;
  private final SqlSession sqlSessionProxy;
  private final PersistenceExceptionTranslator exceptionTranslator;

    /**
     * 我们可以看到SqlSessionTemplate中创建了一个SqlSession的代理对象
     */
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {
      ...
    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class }, new SqlSessionInterceptor());
  }

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

    //代理方法主要是在执行数据库操作之后把session关闭,这就是为什么mybaits的一级缓存在spring中会失效,因为每次操作之后都会把session关闭
  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);
        ...
        return result;
      } catch (Throwable t) {
        ...
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }
}

public final class SqlSessionUtils {
    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {
    ...

    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    //第一次获取的时候肯定是null
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    //实际上就是执行了sessionFactory.openSession获取一个新的session
    session = sessionFactory.openSession(executorType);

    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
}

MapperFactoryBean的实例化过程中我们重点关注SqlSessionTemplate,SqlSessionTemplate中又对SqlSession做了一层代理,其目的是在每次执行完数据库操作后关闭session,当然MapperFactoryBean中还有在afterPropertiesSet中解析Mapper中注解类型的sql语句,这个会专门挑出来,和xml解析一起写一篇笔记。接下来我们看一下我们在getMapper时,底层源码干了什么事情。

我们都知道在获取容器中的factoryBean时(如果获取factoryBean本身,可以通过(&beanName)来获取),实际上是调用factoryBean.getObject,那么我们来看看MapperFactoryBean.getObject做了什么。

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  @Override
  public T getObject() throws Exception {
      //getSqlSession 就是返回sqlSessionTemplate
    return getSqlSession().getMapper(this.mapperInterface);
  }
}

public class SqlSessionTemplate implements SqlSession, DisposableBean {
  @Override
  public <T> T getMapper(Class<T> type) {
      //getConfiguration()返回的就是我们在sqlSessionFactoryBean中配置的Configuration对象
    return getConfiguration().getMapper(type, this);
  }
}

public class Configuration {
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }
}

public class MapperRegistry {
  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      //mapperProxyFactory在下篇笔记中会分析其具体生成过程
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    ...
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
}

/**
 * 返回一个mapper代理对象
 */
public class MapperProxyFactory<T> {
  @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<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

我们可以看出,其实我们在获取mapper实例时,返回的是mapper的代理对象,接下来我们看一下,在执行mapper的查询方法时,mapperProxy代理方法具体做了什么操作。

public class MapperProxy<T> implements InvocationHandler, Serializable {

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        //符合以下情况直接执行
      if (Object.class.equals(method.getDeclaringClass())) {
          //执行继承或重写Object的方法,如equals等
        return method.invoke(this, args);
      } else if (method.isDefault()) {
          //这里执行的是接口中的默认方法
        ...
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
      //这里的sqlSession是sqlSessionTemplate
    return mapperMethod.execute(sqlSession, args);
  }
    
    //解析执行方法的信息,包括sql语句,参数,返回值等等,放入map中并返回。具体下篇笔记会记录。
    private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  }

}

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      ...
      case SELECT:
        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);
            //我们重点看这个方法,这里执行的是sqlSessionTempalte的selectOne,那我们看看selectOne具体是怎么实现的
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
       ...
    }
   ...
    return result;
  }

}

public class SqlSessionTemplate implements SqlSession, DisposableBean {
    @Override
  public <T> T selectOne(String statement, Object parameter) {
      //是通过sqlSessionProxy执行selectOne,前面我们已经分析过sqlSessionProxy,代理的主要作用就用于执行操作后关闭session
    return this.sqlSessionProxy.selectOne(statement, parameter);
  }
}

最后总结一下,Mapper实例化的主要流程就是:

MapperScannerRegistrar 注册 ClassPathMapperScanner -->

ClassPathMapperScanner 扫描 @MapperScan 路径下配置的所有接口,并设置类型为MapperFactoryBean,同时设置自动注入 -->

MapperFactoryBean 实例化时,会调用setSqlSessionFactory实例化sqlSessionTemplate-->

getBean获取mapper时,会创建一个创建一个代理对象-->

调用mapper方法时,会调用MapperMethod.invoke(jdk动态代理),最终调用哪个sqlSessionTemplate中sqlSessionProxy的方法,然后关闭session。

以上是关于mybaits-spring 源码阅读随笔的主要内容,如果未能解决你的问题,请参考以下文章

Python代码阅读(第19篇):合并多个字典

Python代码阅读(第26篇):将列表映射成字典

如何进行 Java 代码阅读分析?

Python代码阅读(第41篇):矩阵转置

Python代码阅读(第25篇):将多行字符串拆分成列表

JDK源码阅读之 HashMap