Mybatis与Spring整合部分源码分析

一、@MapperScan注解解析

@MapperScan(basePackages = "com.ziroom.springboot.springbootsourcetest.mapper")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
    
    

MapperScan注解包含了@Import注解,作用是加载MapperScannerRegistrar对象

@Import 在springboot中是非常重要的,主要作用是加载指定类到spring容器中

MapperScannerRegistrar,java 同时实现了ImportBeanDefinitionRegistrar接口,该接口的实现方法是用来注册BeanDefinitions的,配合@Import注解,会调用ImportBeanDefinitionRegistrar接口的方法registerBeanDefinitions

二、MapperScannerRegistrar实现

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

该部分主要是获取到注解**@MapperScan**

void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
    BeanDefinitionRegistry registry, String beanName) {
    
    
  BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
  ...  //省略部分代码
    // 2、设置属性
    String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
    
    
      builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
    }
  
    //3、设置扫描的包
    List<String> basePackages = new ArrayList<>();
    basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
        .collect(Collectors.toList()));
  
  	//4、当前注解所在的包路径
  	if (basePackages.isEmpty()) {
    
    
      basePackages.add(getDefaultBasePackage(annoMeta));
    }
    
    // 5、将当前封装的BeanDefinition对象注册
	  registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
  
}

BeanDefinitionBuilder该对象是spring中对对象的描述,包含了对象的所有信息

1、创建MapperScannerConfigurer对象的BeanDefinitionBuilder对象,该对象包含了mybatis以及整合spring的所有的配置信息

basePackage

sqlSessionFactory

sqlSessionTemplate

2、AnnotationMetadata annoMeta是对注解MapperScan的封装,后面很多的代码都是将annoMeta的数据封装到BeanDefinitionBuilder对象中,后面在实例化MapperScannerConfigurer对象时进行属性赋值

3、设置mapper扫描的包

4、如果在注解@MapperScan里没有设置扫描的包路径,则会默认使用注解所在包的路径

5、将当前封装的BeanDefinition对象注册

三、MapperScannerConfigurer的BeanDefinition加载

该类实现了接口BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,这个方法是在BeanDefinition被注册后执行的

@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);
  
  ...
    
  // 关键步骤,对mapper进行扫描
  scanner.scan(
      StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

该方法主要是为了构建ClassPathMapperScanner对象,扫描指定的mapper,这里关键步骤在最后一步

四、扫描mapper–ClassPathMapperScanner

BeanDefinitionHolder BeanDefinition的包装类,包含了BeanDefinition和BeanName信息

1、类ClassPathMapperScanner继承了spring的类ClassPathBeanDefinitionScanner,重写了doScan方法

@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    
    
  // 调用的是父类ClassPathBeanDefinitionScanner的方法
  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;
}

2、在调用父类的doScan时,会扫描到所有的mapper,将这些mapper进行组装成BeanDefinitionHolder,在返回集合的同时,将BeanDefinition生成代理对象并且注册到容器中(这里不是实例,还是BeanDefinition对象)

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    
    
  
  			...  省略部分代码		
  
				if (checkCandidate(beanName, candidate)) {
    
    
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
          // 代理对象
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
          // 注册
					registerBeanDefinition(definitionHolder, this.registry);
				}
	  return beanDefinitions;
}

最后返回扫描的所有mapper的BeanDefinitionHolder集合

3、在获取代理对象这里进行几轮的跳转调用,进入AOP相关代理封装

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
    
    
    String originalBeanName = definition.getBeanName();
    BeanDefinition targetDefinition = definition.getBeanDefinition();
    String targetBeanName = getTargetBeanName(originalBeanName);
    RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
    proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
    proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
    proxyDefinition.setSource(definition.getSource());
    proxyDefinition.setRole(targetDefinition.getRole());
    proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
    if (proxyTargetClass) {
    
    
        targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
    } else {
    
    
        proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
    }

    proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
    proxyDefinition.setPrimary(targetDefinition.isPrimary());
    if (targetDefinition instanceof AbstractBeanDefinition) {
    
    
        proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition)targetDefinition);
    }

    targetDefinition.setAutowireCandidate(false);
    targetDefinition.setPrimary(false);
    registry.registerBeanDefinition(targetBeanName, targetDefinition);
    return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}

4、返回扫描后的mapper的BeanDefinitionHolder集合后,又对每一个BeanDefinitionHolder进行注入配置,因为在这之前只是记录了接口相关的信息,还需要注入实现类以及一些属性值

private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    
    
  AbstractBeanDefinition definition;
  BeanDefinitionRegistry registry = getRegistry();
  for (BeanDefinitionHolder holder : beanDefinitions) {
    
    
    definition = (AbstractBeanDefinition) holder.getBeanDefinition();
    boolean scopedProxy = false;

    String beanClassName = definition.getBeanClassName();
    definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
    definition.setBeanClass(this.mapperFactoryBeanClass);  // 设置实现类
    definition.setLazyInit(lazyInitialization);
    if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
    
    
        definition.setScope(defaultScope);
      }
   }
}

关键一步,设置BeanClass为mapperFactoryBeanClass

五、Mapper实例化

在实例化接口代理对象时,实际上是实例化的MapperFactoryBean,该类的顶级父类是spring的DaoSupport,他实现了InitializingBean接口,也就是在对象被实例化的时候会执行afterPropertiesSet方法

public abstract class DaoSupport implements InitializingBean {
    
    
    protected final Log logger = LogFactory.getLog(this.getClass());

    public DaoSupport() {
    
    
    }
    public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
    
    
        this.checkDaoConfig();
        try {
    
    
            this.initDao();
        } catch (Exception var2) {
    
    
            throw new BeanInitializationException("Initialization of DAO failed", var2);
        }
    }

    protected abstract void checkDaoConfig() throws IllegalArgumentException;

    protected void initDao() throws Exception {
    
    
    }
}

这里通过模板模式去调用checkDaoConfig方法

在MapperFactoryBean类中实现了checkDaoConfig方法,这里主要是将扫描的mapper接口加入到myabtis中

@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 {
    
    
      // 添加mapper
      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();
    }
  }
}

this.mapperInterface 扫描到的class类对象

六、总结

mybatis和spring的整合实际上就是将mapper交给spring管理,也就是将mapper注入到IOC容器中

BeanDefinition是对扫描到的bean的描述,每个类都有一个BeanDefinition对象一一对应,spring中所有的Bean实例化都是通过该对象生成的

在整合的过程中会出现多种BeanDefinition的包装类,都是用来生产spring Bean描述对象BeanDefinition的

1、@MapperScan注解在被加载时,会触发@Import注解

2、触发MapperScannerRegistrar类的registerBeanDefinitions方法

3、在registerBeanDefinitions方法中会注册MapperScannerConfigurer对象,同时触发MapperScannerConfigurer注册后会触发注册通知方法postProcessBeanDefinitionRegistry

4、在postProcessBeanDefinitionRegistry方法中会创建ClassPathMapperScanner类,该类继承自spring的扫描类

5、ClassPathMapperScanner的scan方法会去扫描指定包下的mapper,同时将mapper的代理对象MapperFactoryBean注入

6、最后将mapper组装好的BeanDefinition对象进行注册,完成spring管理

7、后面的操作就和单独使用mybatis一样了,通过注解获取到mapper

七、补充

  1. 在这里我是用注解的形式讲解的,实际上还有XML的方式
  2. 在mybatis中我们使用到的类似于SqlSessionFactory这样的对象,在与spring整合时候是通过xml去配置的这些对象,而在springboot项目中是自动装配的

猜你喜欢

转载自blog.csdn.net/weixin_33613947/article/details/110491301
今日推荐