在项目中大多数用的都是@MapperScan注解,指定basePackages,扫描mybatis Mapper接口类,另外一种方式是用@Mapper注解,其实这两种方法扫描配置用的是一个地方,只是扫描入口不同。
@SpringBootApplication
@MapperScan("cn.xh.kaierle.admin.dao")
@EnableScheduling
public class Application
@MapperScan是根据其注解上MapperScannerRegistrar进行自动配置的,最终调用的自动配置代码和下面的代码一致
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
public @interface MapperScan
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar接口可以将自定义的对象注册为spring管理的bean
@Mapper自动配置的程序入口是 MybatisAutoConfiguration类的最下面,位置在mybatis-spring-boot-starter包下面的mybatis-spring-boot-autoconfigure,根据名字可以看出,这个是自动配置,这个地方用到了spring-boot的自动配置相关注解
1 全局配置入口
上面代码的逻辑是 如果标注了@MapperScan 的注解,将会生成 MapperFactoryBean, 如果没有标注@MapperScan 也就是没有MapperFactoryBean的实例,就走@Import里面的配置,下面看看AAI的配置,它是MybatisAutoConfiguration类下的内部类
2 标记有 @mapper注解的类 扫描配置
上面的代码的逻辑就是初始化ClassPathMapperScanner扫描器,这个扫描器继承了spirng的ClassPathBeanDefinitionScanner的主要作用就是扫描Mapper接口类进行配置并注册为spring bean,首先看看核心的doScan()方法,@MapperScan注解的自动配置也是用了这个注解,这个方法做了什么,也是贴图如下:
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
public static final Logger LOGGER = LoggerFactory.getLogger(MapperScannerRegistrar.class);
private ResourceLoader resourceLoader;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 省略代码
scanner.registerFilters(); // 注册过滤器,过滤不需要的bean
scanner.doScan(StringUtils.toStringArray(basePackages));
}
scanner.registerFilters(); 作用
private final List<TypeFilter> includeFilters = new ArrayList<>();
private final List<TypeFilter> excludeFilters = new ArrayList<>();
/**
* Determine whether the given class does not match any exclude filter
* and does match at least one include filter.
* @param metadataReader the ASM ClassReader for the class
* @return whether the class qualifies as a candidate component
*/
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
3 ClassPathMapperScanner类红的核心方法 doScan()
这个类主要就是调用父类ClassPathBeanDefinitionScanner中的doScan()方法,获取所有的类定义,之后添加自己的逻辑:processBeanDefinitions()
下面看看processBeanDefinitions()方法主要做了什么,也是贴出核心代码
private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
//设置bean真正的class类型MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass());
//设置通用 Mapper
if(StringUtils.hasText(this.mapperHelperBeanName)){
definition.getPropertyValues().add("mapperHelper", new RuntimeBeanReference(this.mapperHelperBeanName));
} else {
//不做任何配置的时候使用默认方式
if(this.mapperHelper == null){
this.mapperHelper = new MapperHelper();
}
definition.getPropertyValues().add("mapperHelper", this.mapperHelper);
}
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
4 ClassPathMapperScanner类中处理类定义的核心方法processBeanDefinitions()
注意上面涂黄的代码,这个方法主要做的事情就是重新设置上面扫描出的BeanDefinition,设置构造器的参数,构造器的参数为标注了@Mapper的类的class,
做的事情其实是重写BeanDefinition的BeanClass字段为MapperFactoryBean.class,并且将beanClass其实也就是MapperFactoryBean的构造器参数设置为实际的标注了@Mapper的接口,之后当Mapper接口注入的时候,实际调用的是MapperFactoryBean中的getObject()获取特定的mapper实例
如下是MapperFactoryBean的核心逻辑,mapperInterface字段是通过上面的
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());这段代码注入的
5 MapperFactoryBean的核心代码
可见这个类实现了FactoryBean的接口(自由拓展注入的类),以及继承了SqlSessionDaoSupport,继承SqlSessionDaoSupport的主要目的是为了获取SqlSession,通过SqlSession获取具体的mapper代理类(这个也是核心方法)
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;
}
/**
* {@inheritDoc}
*/
@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();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
/**
* {@inheritDoc}
*/
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSingleton() {
return true;
}
}
综上所述,首先根据标注的@MapperScan 获取basePackage或者根据@Mapper获取所在packages,之后通过 ClassPathMapperScanner去扫描包,获取所有Mapper接口类的BeanDefinition,之后具体配置,设置beanClass为MapperFactoryBean,设置MapperFactoryBean的构造器参数为实际的Mapper接口类,通过ClassPathBeanDefinitionScanner父类进行bean注册,自动注入的时候,就会调用MapperFactoryBean的getObject方法获取实际类型的实例。