上篇文章讲到MapperScannerConfigurer的postProcessBeanDefinitionRegistry()方法,本文继续深入该方法。
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 的一种工具,该工具是为了解决数据分析任务而创建的。
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了。
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); } // 省略部分代码 }
MapperFactoryBean实现了FactoryBean,我们都知道,当调用spring的getBean()方法时,如果当前对象是FactoryBean类型的话,spring会调用他的getObject()方法。可以看到MapperFactoryBean的getObject()实际就是Mybatis的处理逻辑;
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的处理逻辑了。
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的操作。
以上仅是我自己的理解,如有不对的地方还请指正,共同学习进步。