ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring整合Mybatis源码解析:@MapperScan原理(二)

2021-12-16 20:00:18  阅读:215  来源: 互联网

标签:Mapper MapperFactoryBean definition Spring mapperInterface MapperScan 源码 configu


文章目录


前言

上篇文章讲到MapperScannerConfigurer的postProcessBeanDefinitionRegistry()方法,本文继续深入该方法。


一、Mapper注册过程

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    //省略部分代码
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
		// 重点看doScan()
		doScan(basePackages);
		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  	// 调用父类的doScan()方法,根据我们在@MapperScan中配置的路径扫描所有的Mapper并封装成BeanDefinitionHolder
    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 {
    // 处理扫描到的bd,最关键一步
      processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
  }

到这里我们已经拿到了所有的Mapper并封装成了BeanDefinitionHolder,后面就是处理BeanDefinitionHolder最关键的一步,我们来看一下具体是怎么处理的。

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

二、processBeanDefinitions()解析过程

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      // 省略部分代码
      String beanClassName = definition.getBeanClassName();
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); 
      definition.setBeanClass(this.mapperFactoryBeanClass);
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
   	  // 省略部分代码
    }
  }

该方法内会遍历所有的Mapper,最关键的处理有两步:
1.添加一个构造函数的参数值:beanClassName,也就是当前Mapper的全限定类名。
2.设置bd的BeanClassMapperFactoryBean.class,这里来了一波偷梁换柱,为什么这么做呢?那就必须要看看MapperFactoryBean了。


三、MapperFactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  private Class<T> mapperInterface;
  private boolean addToConfig = true;
  public MapperFactoryBean() {
    // intentionally empty
  }
  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

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

  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  // 省略部分代码
}

1.FactoryBean

MapperFactoryBean实现了FactoryBean,我们都知道,当调用spring的getBean()方法时,如果当前对象是FactoryBean类型的话,spring会调用他的getObject()方法。可以看到MapperFactoryBeangetObject()实际就是Mybatis的处理逻辑;

2.InitializingBean

MapperFactoryBean还继承了SqlSessionDaoSupport ,看下具体继承关系:

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>
public abstract class SqlSessionDaoSupport extends DaoSupport
public abstract class DaoSupport implements InitializingBean

也就是说MapperFactoryBean还是InitializingBean类型。spring在初始化对象时候,如果当前对象是InitializingBean类型,那么spring会调用其afterPropertiesSet()方法。

public abstract class DaoSupport implements InitializingBean {
	/** Logger available to subclasses. */
	protected final Log logger = LogFactory.getLog(getClass());
	@Override
	public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
		// 这里是模板模式,具体处理逻辑放在子类实现里面
		checkDaoConfig();
		// 省略部分代码
	}
	// 省略部分代码
}

再来看下MapperFactoryBean的实现:

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

可以看到MapperFactoryBean会在这里首先尝试获取Mapper(当然取不到了),获取不到则调用configuration.addMapper(this.mapperInterface),这里面就是Mybatis的处理逻辑了。

3.this.mapperInterface

this.mapperInterface是哪里来的呢?前面提到的对扫描出来的db处理就派上用场了:

definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);

看下MapperFactoryBean的构造函数:

 public MapperFactoryBean() {
    // intentionally empty
  }

  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

看到这里一切的清晰了。

总结

到这里对@MapperScan的解析就完毕了,其实过程还是不复杂的,首先跟我我们配置的扫描包路径去获取所有的Mapper并封装成BeanDefinitionHolder集合,然后遍历整个集合,将Mapper的类型设置为MapperFactoryBean,利用MapperFactoryBean去完成Mybatis的操作。
以上仅是我自己的理解,如有不对的地方还请指正,共同学习进步。

标签:Mapper,MapperFactoryBean,definition,Spring,mapperInterface,MapperScan,源码,configu
来源: https://blog.csdn.net/wanganmengyy/article/details/121981630

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有