mybatis整合到spring的自动配置流程

本文简单讲述mybatis整合到spring的自动配置流程。

首先,当我们引入mybatis-spring-boot-starter的时候,可以看到其pom.xml文件到中包含

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>

这个依赖就是自动配置的相关代码。在mybatis-spring-boot-autoconfigure的META-INF目录下,可以看到spring.factories文件。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

熟悉springboot starter原理的都知道这个文件。接下来,我们主要关注MybatisAutoConfiguration这个类。该类有两个内部类AutoConfiguredMapperScannerRegistrarMapperScannerRegistrarNotFoundConfigurationMapperScannerRegistrarNotFoundConfiguration上有@import注解引用了AutoConfiguredMapperScannerRegistrar

AutoConfiguredMapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法,在该方法中注册了一个MapperScannerConfigurer的BeanDefinition。

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor,可将额外的BeanDefinition注册到spring容器中。

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  ...
  // 将额外的BeanDefinition注册到spring beanDefinition容器中
  scanner.scan(
      StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

需要注意的是ClassPathMapperScanner 实例scanner的scan方法在其父类,但是调用的doScan方法在该类被重写了。

public int scan(String... basePackages) {
   int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
   // 获取所有的Mapper,生成beanDefinition
   doScan(basePackages);

   // Register annotation config processors, if necessary.
   if (this.includeAnnotationConfig) {
   // 将Mapper对应的beanDefinition注册到beanDefinition容器中
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
   }

   return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

在doScan方法中,首先调用父类的doScan方法,根据配置的basePackages扫描所有的Mapper并生成beanDefinition的集合,接着调用了processBeanDefinitions(beanDefinitions)方法对这些beanDefinition进行处理。

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);
    
    ...
  }
}

该方法最主要的是将动作

  1. 将Mapper的接口加到BeanDefinition的构造器参数中
  2. 把BeanDefinition的beanClassName替换为MapperFactoryBean.class

以上流程,大致上完成了将Mapper注册到spring容器。

接下来讲讲MapperFactoryBean这个类

在这里插入图片描述

可以看到,该类实现了InitializingBean和FactoryBean两个接口。因此要重点关注InitializingBean的afterPropertiesSet方法和FactoryBean的getObject方法的实现。在DaoSupport类中,可以看到,afterPropertiesSet主要调用了checkDaoConfig方法。

@Override
protected void checkDaoConfig() {
  super.checkDaoConfig();

  notNull(this.mapperInterface, "Property 'mapperInterface' is required");

  Configuration configuration = getSqlSession().getConfiguration();
  if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
    try {
      configuration.addMapper(this.mapperInterface);
    } catch (Exception e) {
      logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
      throw new IllegalArgumentException(e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}

而getObject方法的实现如下

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

跟踪checkDaoConfig方法中的configuration.addMapper(this.mapperInterface)代码,到MapperRegistry.addMapper方法。

public <T> void addMapper(Class<T> type) {
  ...
      knownMappers.put(type, new MapperProxyFactory<>(type));
  ...
}

这个MapperProxyFactory就是一个JDK动态代理的工厂类。

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);
  }
}

getObject方法最终将调用newInstance方法,获得一个代理对象。

再来看看MapperProxy这个类,该类实现了InvocationHandler类,因此主要看实现的invoke方法,这里就不在叙述了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值