mybatis源码分析、底层原理

mybatis用于和数据库交互层面,我们只需定义好Mapper接口,在业务层需要的地方通过@Autowird引入对应的XxxMapper即可,很方便。为什么@Autowird可以从容器中拿到XxxMapper对象呢?其实是mybatis使用动态代理帮我们生成了XxxMapper的代理对象!下面通过源码来看一下mybatis是如何操作的

1.从@MapperScan说起

@MapperScan属于mybatis的扫描包注解,主要是扫描XxxMapper,下面看@MapperScan的源码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)    //这里导入了MapperScannerRegistrar
public @interface MapperScan {
  String[] value() default {};
  ......
}

注意@MapperScan注解上面的@Import(MapperScannerRegistrar.class)
在这里导入了MapperScannerRegistrar类,这个类在容器初始化之前会被执行,它主要通过扫描@MapperScan中指定包名,通过层层调用,来生成当前包下XxxMapper的代理对象,并放入容器中。下面来看MapperScannerRegistrar的源码。

MapperScannerRegistrar 类实现了ImportBeanDefinitionRegistrar接口,
在这里插入图片描述
MapperScannerRegistrar的源码
在registerBeanDefinitions()方法中,主要是扫描@MapperScan中标注的包中的内容,把MapperFactoryBean注入容器

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  private ResourceLoader resourceLoader;

  /**
   * {@inheritDoc}
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  
	//***获取@MapperScan注解标注类的信息
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    // this check is needed in Spring 3.1
    if (resourceLoader != null) {
      scanner.setResourceLoader(resourceLoader);
    }

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

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }

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

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    
    //***取到@MapperScan里边的值(即包名)
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }

    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    
    //***spring的框架,暂时不说
    scanner.registerFilters();
    
    //***扫描basePackages(com.luban...)里的内容,源码如下“
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

}

扫描basePackages(com.luban…)里的内容使用的doScan方法
doScan源码:主要是调用了spring的doScan方法(而非mybatis的),结果不是mybatis想要的,但是拿到了对应类型的BeanDefinitionHolder(bd),再由processBeanDefinitions转成Mybatis想要的

  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  
//注意:这里的super.doScan调用的是spring的方法,
		扫描某个包(basePackages)得到对应类型的BeanDefinitionHolder(bd),但是这里的(bd)类型不对
		不是我们想要的,下面的processBeanDefinitions方法才是mybatis对这个bd做的处理,做成想要的bd。
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
    
    //mybatis处理上边spring扫描的BeanDefinitionHolder(bd),成为mybatis想要的!
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }

怎么处理的呢 ,mybatis的processBeanDefinitions源码如下
主要作用:遍历上边spring扫描的每一个XxxMapper,给每一个XxxMapper生成对应的MapperFactoryBean

  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;

	//*****循环传进来的spring的doscan方法生成的bd
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }

      // the mapper interface is the original class of the bean   // mapper接口是Bean的原始类
      // but, the actual class of the bean is MapperFactoryBean //,但是,该Bean的实际类是MapperFactoryBean
      
      //****添加构造方法,设置接口名字为bean的类型
      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      
      //****在bd中设置mybatis中的FactoryBean == > mapperFactoryBean ,接下来看mapperFactoryBean 的源码
      definition.setBeanClass(this.mapperFactoryBean.getClass());

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        if (logger.isDebugEnabled()) {
          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        }
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

mybatis中的FactoryBean == > MapperFactoryBean 的源码
主要作用:为上边生成的每一个MapperFactoryBean ,通过 getObject() 方法获取每一个Mapper的代理对象,放入容器

//****implements FactoryBean实现了FactoryBean,注入容器中的对象是 getObject() 中返回的对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {


//****未指定类型的XxxMapper
  private Class<T> mapperInterface;
  
	......
	
	//*****通过构造方法注入XxxMapper,给mapperInterface赋值
  public MapperFactoryBean(Class<T> mapperInterface) {
     this.mapperInterface = mapperInterface;
  }
  
	......
	
	//*****调用Mybatis的getSqlSession().getMapper(this.mapperInterface)方法,生成当前mapper的代理对象
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

2.Mybatis动态代理总结:

1.mybatis定义一个注解@MapperScan
2.在@MapperScan中通过@Import导入了MapperScannerRegistrar,使这个类在启动时被加载。
3.在MapperScannerRegistrar类中,先通过spring扫描得到多个BeanDefinition(bd),后由mybatis的processBeanDefinitions()方法处理上边的多个bd
4.遍历每一个处理后的bd,并添加构造方法,设置接口名字为bean的类型,至此bean类型确定。
5.为每一个处理后的bd,设置FactoryBean(其实就是MapperFactoryBean),
6.在MapperFactoryBean中的getObject()方法中,通过动态代理获取每一个XxxMapper的代理对象,因为上文@Import导入了MapperScannerRegistrar,所以这些代理对象在启动时会被加入到容器,后续在业务层我们就可以随时调用了!

至此,MyBatis通过MapperFactoryBean为每一个XxxMapper生成代理对象结束!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值