@MapperScan 源码解析

@MapperScan 源码解析MapperScan Retention RetentionPol RUNTIME Target ElementType TYPE Documented Import MapperScanne class public interface MapperScan Import

@MapperScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
... }

@Import 的作用就是向spring容器中导入一个BeanDefinition对象

MapperScannerRegistrar

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { 
... }

通过源码看出这个导入的类还是一个ImportBeanDefinitionRegistrar,这个接口下面有一个registerBeanDefinitions(…)
通过这个方法的 BeanDefinitionRegistry ,就可以完成BeanDefinition 的注册

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 

// 获取MapperScan注解的信息
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
//创建一个扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

// this check is needed in Spring 3.1
if (resourceLoader != null) {

scanner.setResourceLoader(resourceLoader);
}

//annotationClass 的设置,也就是扫描出来的类必须标注了annotationClass ,否则不会扫描
Class annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {

scanner.setAnnotationClass(annotationClass);
}

Class markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {

scanner.setMarkerInterface(markerInterface);
}

Class generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {

scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}

Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {

scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}

scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
// 获取指定的sqlSessionFactoryRef 在@MapperScan注解上有一个 SqlSessionFactoryRef的属性可以指定
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

List basePackages = new ArrayList();
for (String pkg : annoAttrs.getStringArray("value")) {

if (StringUtils.hasText(pkg)) {

basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {

if (StringUtils.hasText(pkg)) {

basePackages.add(pkg);
}
}
for (Class clazz : annoAttrs.getClassArray("basePackageClasses")) {

basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}

获取MapperScan注解上的一些信息

定义一个扫描器 ClassPathMapperScanner ,这个扫描器其实是继承自spring的ClassPathBeanDefinitionScanner,但是在实例化的时后
第二个参数值为false , super(registry, false); 意思是说不适用spring提供的默认的过滤方式
对于spring的扫描器而言,需要满足两个条件才会被扫描,首先不能是接口,其次是不需要标注@Component注解,所以这里想要扫描
mapper 的接口就需要自己去实现过滤的规则,到那时这里的annotationClass 只是记录一下,具体的操作在registerFilters()
3.注册Filters
4.进行doScan 扫描

还有一点需要注意的是,spring在进行扫描的时候,会去调用一个方法isCandidateComponent(),这个方法在spring的scanner中的实现如下

	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { 

AnnotationMetadata metadata = beanDefinition.getMetadata();
// 不是接口或抽象类,如果是抽象类那么抽象类上得是Lookup注解
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

这个方法可以进行接口的过滤,但是mybatis的接口是需要扫描到spring 容器的,所以对于@MapperScan的扫描器重写了这个方法

  @Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {

return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}

也就是@MapperScan 中的扫描器只会扫描接口

接下来思考一个问题,通过上述方式的确是能让spring帮助mybatis进行扫描,但是扫描出来的BeanClass是什么呢??? 是mapper接口???
显然不能是这些,接口是无法直接进行实例化的,所以这些扫描出来的BeanDefinition还需要进行处理
处理的逻辑就在doScan()==>processBeanDefinitions()

private void processBeanDefinitions(Set beanDefinitions) { 

GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {

definition = (GenericBeanDefinition) holder.getBeanDefinition();

if (logger.isDebugEnabled()) {

logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
}

//修改构造方法的参数
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());//修改BeanClass

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) {

if (logger.isDebugEnabled()) {

logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}

主要关注两行
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBean.getClass());
第一步是修改了构造方法的参数,就是在通过构造方法实例化的时候的参数,那么mapperFactoryBean是什么呢?

public class MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean { 

private Class mapperInterface;

public MapperFactoryBean() {

//intentionally empty
}

public MapperFactoryBean(Class mapperInterface) {

this.mapperInterface = mapperInterface;
}

@Override
public T getObject() throws Exception {

return getSqlSession().getMapper(this.mapperInterface);
}

@Override
public Class getObjectType() {

return this.mapperInterface;
}
...
...
}

可以看出来MapperFactoryBean 就是一个 FactoryBean,并且提供了一个构造方法,然后再进行构造的时候出入进来的依然还是接口的类型,但是实际的类型就不一样了,因为FactoryBean 可以通过getObject的方法来返回实际的类型
可以看到getObject 返回的是 getSqlSession().getMapper(this.mapperInterface),SqlSession在mybatis是可以返回一个Mapper的实现类的,所以真正的对象的创建,代理依然是Mybatis完成的,spring只不过是做对象的管理而已

但是这里是直接 getSqlsession 的,那么这个SqlSession是哪里来的?
在Mybatis中 SqlSession是需要通过SqlSessionFactory 这个对象生成出来的

回到processBeanDefinitions()

 //当前的ClasspathMapperScanner中有没有指定 sqlSessionFactoryBeanName,如果有
//就修改BeanDefinition中的参数值,也就是说在实例化的时候,机会通过set方法去出入这个参数值
//这样就有了一个SqlSessionFactory 了
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) {

if (logger.isDebugEnabled()) {

logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
//如果说在spring中没有指定SqlSessionFactoryBean ,那么就会将当前Bean的自动注入的模型改为by_type,那么
//在实例化当前bean的时候,就会自动注入通过byType找到的Bean
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
编程小号
上一篇 2025-03-01 12:01
下一篇 2025-04-03 14:30

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/hz/116267.html