Spring整合Mybatis的源码解读——@MapperScan注解与SqlSessionTemplate的线程安全实现

本文深入探讨了Spring整合Mybatis时,如何通过@MapperScan注解将Mapper接口注入到IoC容器,并详细解析了SqlSessionTemplate如何利用线程局部变量SqlSessionHolder实现线程安全。
摘要由CSDN通过智能技术生成

回顾一下Mybatis的Mapper接口实例的生成与执行流程

众所周知,我们基于MyBatis 操作数据库,实际上就是通过 SqlSession 获取一个 JDBC 连接调用api来操作。
1、SqlSession接口。SqlSession 接口有常用的实现类有:DefaultSqlSession(线程不安全)、SqlSessionTemplate (spring中线程安全类,接下来的主角)

/*
 * The default implementation for {@link SqlSession}.
 * Note that this class is not Thread-Safe.
 *
 * @author Clinton Begin
 */
public class DefaultSqlSession implements SqlSession {
   }

2、我们通过sqlsession获取mapper接口实例的基本过程如下:
获取Mapper
通过SqlSession的getMapper(type)方法获取的Mapper接口实例,这个实例是通过MapperProxyFactory生成的代理实例MapperProxy同时实现了invocationhandler接口。

3、MapperProxy的方法执行,在它的invoke方法中调用SqlSession的具体api来执行的。

public class MapperProxy<T> implements InvocationHandler, Serializable {
   
//通过传入的sqlsession进行方法调用
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
    try {
   
      if (Object.class.equals(method.getDeclaringClass())) {
   
        return method.invoke(this, args);
      } else {
   
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
   
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

这个过程用到JDK的动态代理设计模式。

今天的问题:

问题1、spring整合mybatis后是如何把Mapper代理对象注入ioc容器的呢?

  • 基于注解@MapperScan和@Mapper,前者是spring的注解,后者是mybatis的注解
  • 基于Xml配置文件

今天我们讨论的是基于@MapperScan注解的方式!

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

我们通过@MapperScan(“com.xxx.xxx”)扫描package把Mapper接口生成实例并注入IOC容器。扫描注册的类就是通过MapperScannerRegistrar.class实现的!

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

  void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
      BeanDefinitionRegistry registry, String beanName) {
   
//获取MapperScannerConfigurer.class的BeanDefinitionBuilder
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    builder.addPropertyValue("processPropertyPlaceHolders", true);

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
   
      builder.addPropertyValue("annotationClass", annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
   
      builder.addPropertyValue("markerInterface", markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
   
      builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
   
      builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
    }

    String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
   
      builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
    }

    String sqlSessionFactoryRef = annoAttrs.ge
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值