- 前言
- 一、Mapper注册过程
- 二、processBeanDefinitions()解析过程
- 三、MapperFactoryBean
- 1.FactoryBean
- 2.InitializingBean
- 3.this.mapperInterface
- 总结
前言
上篇文章讲到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 SetdoScan(String... basePackages) { // 调用父类的doScan()方法,根据我们在@MapperScan中配置的路径扫描所有的Mapper并封装成BeanDefinitionHolder Set 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(SetbeanDefinitions) { 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的BeanClass为MapperFactoryBean.class,这里来了一波偷梁换柱,为什么这么做呢?那就必须要看看MapperFactoryBean了。
三、MapperFactoryBean
public class MapperFactoryBean1.FactoryBeanextends SqlSessionDaoSupport implements FactoryBean { private Class mapperInterface; private boolean addToConfig = true; public MapperFactoryBean() { // intentionally empty } public MapperFactoryBean(Class 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); } // 省略部分代码 }
MapperFactoryBean实现了FactoryBean,我们都知道,当调用spring的getBean()方法时,如果当前对象是FactoryBean类型的话,spring会调用他的getObject()方法。可以看到MapperFactoryBean的getObject()实际就是Mybatis的处理逻辑;
2.InitializingBeanMapperFactoryBean还继承了SqlSessionDaoSupport ,看下具体继承关系:
public class MapperFactoryBeanextends SqlSessionDaoSupport implements FactoryBean
public abstract class SqlSessionDaoSupport extends DaoSupport
public abstract class DaoSupport implements InitializingBean
也就是说MapperFactoryBean还是InitializingBean类型。spring在初始化对象时候,如果当前对象是InitializingBean类型,那么spring会调用其afterPropertiesSet()方法。
public abstract class DaoSupport implements InitializingBean {
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.mapperInterfacethis.mapperInterface是哪里来的呢?前面提到的对扫描出来的db处理就派上用场了:
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
看下MapperFactoryBean的构造函数:
public MapperFactoryBean() {
// intentionally empty
}
public MapperFactoryBean(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
看到这里一切的清晰了。
总结到这里对@MapperScan的解析就完毕了,其实过程还是不复杂的,首先跟我我们配置的扫描包路径去获取所有的Mapper并封装成BeanDefinitionHolder集合,然后遍历整个集合,将Mapper的类型设置为MapperFactoryBean,利用MapperFactoryBean去完成Mybatis的操作。
以上仅是我自己的理解,如有不对的地方还请指正,共同学习进步。



