文章目录
前言
上篇文章讲到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的BeanClass为MapperFactoryBean.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()方法。可以看到MapperFactoryBean的getObject()实际就是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的操作。
以上仅是我自己的理解,如有不对的地方还请指正,共同学习进步。