mybatis架构和 Spring 整合源码解析

mybatis架构:https://www.jianshu.com/p/15781ec742f(引用这位大佬的)

个人总结:

	public static void main(String[] args) throws IOException {
		String resource = "Mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		SqlSession sqlSession = sqlSessionFactory.openSession();
		SqlSession sqlSession2 = sqlSessionFactory.openSession();
		TestMapper mapper = sqlSession.getMapper(TestMapper.class);
		List<Map<String, Object>> maps = mapper.testQuery();
		List<Map<String, Object>> maps1 = mapper.testQuery();
		TestMapper mapper1 = sqlSession2.getMapper(TestMapper.class);
		List<Map<String, Object>> maps2 = mapper1.testQuery();
}

mybatis通过:

		String resource = "Mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

构建一个SqlSessionFactory

默认使用实现DefaultSqlSessionFactory,SqlSessionManager虽然也是它的子类,但是是利用装饰者模式装饰SqlSessionFactory,添加

private final SqlSession sqlSessionProxy;

字段进行SqlSession的管理:

new SqlSessionFactoryBuilder().build(inputStream);传入的是读取Mybatis-config.xml的输入流,其实就是利用mybatis的配置文件,生成一个
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

利用XMLConfigBuilder .parse()生成Configuration(解析XML,生成mybatis的配置信息)

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

而DefaultSqlSessionFactory的唯一属性进行Configuration

SqlSession sqlSession = sqlSessionFactory.openSession();

这个获得一个SqlSession,利用SqlSession去获得Mapper,然后去执行方法

其实不是SqlSession去执行的,二十交给执行器Executor去执行sql

Executor的继承体系:

Executor根据下面的不同类型生成不同的处理,每个处理有不同作用:

public enum ExecutorType {
  SIMPLE, REUSE, BATCH
}

ExecutorType.SIMPLE:这个执行器类型不做特殊的事情。它为每个语句的每次执行创建一 个新的预处理语句。

ExecutorType.REUSE:这个执行器类型会复用预处理语句。

ExecutorType.BATCH:这个执行器会批量执行所有更新语句,也就是jdbc addBatch API的 facade模式。

你们可以自己去看对应的类的源码哈。

咱们在看这个的源码

TestMapper mapper = sqlSession.getMapper(TestMapper.class);
 @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
  @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<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
sqlSession.getMapper(TestMapper.class);方法转交org.apache.ibatis.session.Configuration#getMapper》》org.apache.ibatis.binding.MapperRegistry#getMapper》》org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)》》(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);

其实到最后生成一个代理对象MapperProxy<T>

MapperProxy<T>实现了InvocationHandler,所以最后是利用jDK的动态代理生成一个代理对象。

后面的内容我就不说了前面引用的文章里面有。

补充一点,mybatis 的一级缓存和二级缓存

引用大佬的文章:https://tech.meituan.com/2018/01/19/mybatis-cache.html

Spring 整合Mybatis

spring 怎么扫描 mapper beanDefinition

与其说spring 整合Mybatis不如说Mybatis整合(有历史的缘故,自己百度)

我们在整合的时候会在配置类上加上一个注解@MapperScan,这个注解的用处就大了啦,

所有的Mapper扫面就是因为这个注解才会被扫描到spring bean里面去,咱们进去看这个注解的源码:

这里标注的@Import(MapperScannerRegistrar.class)导入了一个类,这个类的bean什么时候加载的大家自己去看我的Aop源码解析里面哈,跟那个差不多。

咱们在进去这个里面看源码:

因为这个MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,所以在bean扫描@Import或得MapperScannerRegistrar得信息,然后加载bean的时候回去执行:

org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.beans.factory.support.BeanNameGenerator)

方法,咱们在看org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)

源码:

  @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) {

    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.getString("sqlSessionFactoryRef");
    if (StringUtils.hasText(sqlSessionFactoryRef)) {
      builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
    }

    List<String> basePackages = new ArrayList<>();
    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));

    basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
        .collect(Collectors.toList()));

    basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
        .collect(Collectors.toList()));

    if (basePackages.isEmpty()) {
      basePackages.add(getDefaultBasePackage(annoMeta));
    }

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

    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));

    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);z这里面又注册了一个MapperScannerConfigurer的BeanDefinitionBuilder,再看MapperScannerConfigurer的源码:

因为实现了BeanDefinitionRegistryPostProcessor,所以在启动的时候回去先去实例化MapperScannerConfigurer,然后调用:

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry

再看MapperScannerConfigurer#postProcessBeanDefinitionRegistry做了什么:

  @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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(lazyInitialization)) {
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
    }
    scanner.registerFilters();
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);生成了一个

ClassPathMapperScanner,然后调用ClassPathMapperScannerdoScan(String... basePackages)

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {}
ClassPathScanningCandidateComponentProvider就是spring里面用来扫描bean的,并且
ClassPathMapperScanner重写了
  @Override
  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  }

用来判断可以扫描接口,也就是mapper

咱们在看org.mybatis.spring.mapper.ClassPathMapperScanner#doScan方法:

 @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    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 {
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }
  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      definition.setBeanClass(this.mapperFactoryBeanClass);

      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) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }

其实就是利用spring扫描出mapper的BeanDefinitionHolder,然后在在org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions进行变更一下:

definition.setBeanClass(this.mapperFactoryBeanClass);
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
z这里设置bean的类型为MapperFactoryBean(实现了FactoryBean,实现了org.springframework.beans.factory.FactoryBean#getObject)利用getObject()获取对象

 @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

到这里是不是很熟悉了,上面有分析过哈。

definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);这个设置根据类型加载bean哈,也是重要点。

到这里面基本上就可以知道mapper beanDefinition 是怎么存到spring的bean工厂里面的啦。

spring 获取 mapper 对像

其实上面已经分析到了MapperFactoryBean#getObject的方法了,怎么获取就简单了

1.spring 在利用org.springframework.context.support.AbstractApplicationContext#getBean(java.lang.String, java.lang.Class<T>)

TestMapper testMapper = applicationContext.getBean("testMapper", TestMapper.class);

的时候,回去判断beanDefinition 的类型是不是FactoryBean,如果是的话就调用

org.springframework.beans.factory.FactoryBean#getObject)获取对象。

那不又回到我们开始的地方了。

具体可以自己打断点跟踪哈。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值