Spring整合Mybatis之源码简单解析

主配置类如下

@SpringBootApplication
@MapperScan("com.jarvis.mybatis.mapper")  //扫描com.jarvis.mybatis.mapper包下的mapper接口
@EnableCaching
public class MybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisApplication.class, args);
    }
}

先看看@MapperScan 源码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//通过MapperScannerRegistrar.class的registerBeanDefinitions方法将某个注册类(MapperScannerConfigurer)的BeanDefinition注册到spring容器
@Import(MapperScannerRegistrar.class)  
@Repeatable(MapperScans.class)
public @interface MapperScan {
    ...
    String[] basePackages() default {};
    ...
}

@Import用法:https://blog.csdn.net/weixin_43283513/article/details/109840970

>>>启动主配置类,跟踪源码

MapperScannerRegistrar

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     //(图1)解析@MapperScan注解信息,将MapperScan中的属性封装到一个AnnotationAttributes类型的对象中
     AnnotationAttributes mapperScanAttrs = AnnotationAttributes
         .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
     if (mapperScanAttrs != null) {
        // 如果mapperScanAttrs不为空,则调用如下方法进行注册bean定义
        registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
           generateBaseBeanName(importingClassMetadata, 0));
     }
   }
   //注册bean定义!!!
   void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
       BeanDefinitionRegistry registry, String beanName) {
       //创建构造器,用于生成MapperScannerConfigurer的BeanDefinition
       BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
       //builder创建bean定义的一系列步骤
       builder.addPropertyValue("processPropertyPlaceHolders", true);
       ...
       builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
       
       //(图2) 
       // 1. builder.getBeanDefinition():使用构造器生成MapperScannerConfigurer的bean定义
       // 2. registry.registerBeanDefinition:将这个bean定义注册到spring容器
       registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
   }
}

图1:将MapperScan中的属性封装到一个AnnotationAttributes类型的对象中
在这里插入图片描述

图2:生成并注册MapperScannerConfigurer的bean定义
在这里插入图片描述
总结:这一步主要是生成一个MapperScannerConfigurer的BeanDefinition,并注册到容器中。

MapperScannerConfigurer

一、先来看继承关系

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware 
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor

说明MapperScannerConfigurer
   1. 本质是一个BeanFactoryPostProcessor,拥有方法postProcessBeanFactory,在BeanDefinition生成之后、创建原始对象之前执行
   2. 是一个BeanDefinitionRegistryPostProcessor(顾名思义,注册BeanDefinition的后置处理器)
      定义了方法postProcessBeanDefinitionRegistry,但是在什么时候执行?
      很简单,只需要在MapperScannerConfigurer的这两个方法上打个断点debug一下,就可以知道。
      经过debug,可知,先执行了postProcessBeanDefinitionRegistry,然后执行postProcessBeanFactory。
      结论:在MapperScannerConfigurer的BeanDefinition生成之后、创建原始对象之前,会先执行postProcessBeanDefinitionRegistry把扫描包下的mapper接口注册到容器中

二、来看看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();
  //真正去扫描指定包路径下的bean定义信息,会先去调用父类ClassPathBeanDefinitionScanner.scan方法
  //(图3) 扫描路径
  scanner.scan(
      StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

图3:扫描包路径
在这里插入图片描述

扫描包

1. ClassPathBeanDefinitionScanner.scan方法

public int scan(String... basePackages) {
	int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    //跳到ClassPathMapperScanner.doScan方法
	doScan(basePackages);

	// Register annotation config processors, if necessary.
	if (this.includeAnnotationConfig) {
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

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

2. ClassPathMapperScanner.doScan

@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  //通过父类的doScan方法扫描
  //(图4)扫描结果
  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 {
    //如果扫描包下有mapper(即beanDefinitions不为空),则处理这些mapper的BeanDefinition
    processBeanDefinitions(beanDefinitions);
  }

  return beanDefinitions;
}

图4. 扫描结果
在这里插入图片描述
3. processBeanDefinitions:处理bean定义

/**
 * 传入的参数:扫描包下的mapper接口的BeanDefinition
 */
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    //循环我们扫描出的mapper的bean定义,并进行“加工”
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      //获取bean的类名称  如:com.jarvis.mybatis.mapper.BookMapper
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");
          
      /**
      由于mapper是接口,不能被实例化,
      所以这里使用setBeanClass将BeanDefinition中的beanClass属性替换为MapperFactoryBean.class --> 偷天换日
      即所有的mapper的BeanDefinition经过这一步后,类型beanClass都是MapperFactoryBean
      
      MapperFactoryBean实现了FactoryBean接口,这个接口会提供一个getObject()方法,
      当调用getBean方法时,实际内部调用getObject()方法让我们来返回Mapper接口的代理对象。
      
      因为真正要实例化的对象是MapperFactoryBean,这个类中有一个空的构造方法和带一个参数的构造方法(参数为mapperInterface,即动态代理需要的接口),
      并且后面要通过jdk代理生成这个对象的动态代理,所以使用的是带一个参数的构造方法传入接口。
      getConstructorArgumentValues().addGenericArgumentValue(beanClassName)
      将beanClassName--mapper接口名称传入了BeanDefinition的constructorArgumentValues属性中,
        注:constructorArgumentValues属性用于创建原始bean对象的时候根据参数的类型和个数使用对应的构造器实例化。
      后面ioc容器实例化MapperFactoryBean的时候,
      会将beanClassName传入到MapperFactoryBean中构造方法的mapperInterface参数,用于之后jdk动态代理
      */
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); 
      definition.setBeanClass(this.mapperFactoryBeanClass);  
         //定义了private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;

      ...

      if (!explicitFactoryUsed) {
         LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
         /**
          将bean定义的注入模式改为ByType,用于之后注入SqlSessionFactory
         */
         definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      //设置懒加载
      definition.setLazyInit(lazyInitialization);
    }
  }

至此,已经将扫描包下定义的mapper接口的BeanDefinition生成、加工并注册到spring容器中,之后的步骤就是根据beanFactory中的beanDefinitionMap的bean定义进行生成原始对象

MapperFactoryBean:返回动态代理对象

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;
  private boolean addToConfig = true;

  public MapperFactoryBean() {
    // intentionally empty
  }
  //在对mapper接口的bean定义加工时在constructorArgumentValues属性中传入了mapper的接口名,
  //故此会使用该构造方法实例化bean
  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  
  ...
  
  /**
   * 这里返回jdk代理生成的mapper接口的动态代理对象  跳到SqlSessionTemplate.getMapper
   */
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

  ...
}

生成动态代理对象

1. SqlSessionTemplate.getMapper

public <T> T getMapper(Class<T> type) {
  return getConfiguration().getMapper(type, this);
}

2. MybatisMapperRegistry.getMapper

@Override
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //生成一个代理对象的工厂
    final MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
    }
    try {
        //这里返回了代理对象
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

注:
我这里使用的是MybatisPlus,代码和Mybatis是一样的,只是名字不一样,Mybatis中是MapperRegistry类

3. MybatisMapperProxyFactory

//jdk动态代理
protected T newInstance(MybatisMapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
    final MybatisMapperProxy<T> mapperProxy = new MybatisMapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

简单图示

在这里插入图片描述

参考视频:https://www.bilibili.com/video/BV1uJ41117A7?p=3

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值