@MapperScan 源码解析

@MapperScan 源码解析aa

大家好,又见面了,我是你们的朋友全栈君。

@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<? extends Annotation> 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<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) { 

scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> 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<String> basePackages = new ArrayList<String>();
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));
}
  1. 获取MapperScan注解上的一些信息
  2. 定义一个扫描器 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<BeanDefinitionHolder> 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<T> extends SqlSessionDaoSupport implements FactoryBean<T> { 

private Class<T> mapperInterface;
public MapperFactoryBean() { 

//intentionally empty 
}
public MapperFactoryBean(Class<T> mapperInterface) { 

this.mapperInterface = mapperInterface;
}
@Override
public T getObject() throws Exception { 

return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> 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);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/144735.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • java集合类面试题

    java集合类面试题java集合类面试题1.请聊一下java的集合类,以及在实际项目中你是如何用的?注意说出集合体系,常用类,接口,实现类加上你所知道的高并发集合类,JUC在实际项目中引用,照实说就好了2.集合类是怎么解决高并发中的问题?线程非全的集合类ArrayListLinkedListHashSetTreeSetHashMapTreeMap实际开发中我们自己用这样的集合最多,因为一般我们自己写的业务代码中,不太涉及到多线程共享同一个集合的问题线程安全的集合类VectorHashTable

  • Java快排算法(java工程师需要掌握哪些知识)

    思路对于给定的数组,从中选一个元素为比较对象,一般选最左或最右的元素,选左边为升序排,选右边反之。数组array[]:最左边:target=5数组下标:i=0,j=9步骤:①从右边遍历数组,把array[j]比5小的放在5的左边,j–;交换位置后i=0,j=7:②从左边遍历数组,把array[i]比5大的放在5的右边,i++;交换位置后i=…

  • coreseek,Sphinx,elasticsearch,xunsearch 什么关系

    coreseek,Sphinx,elasticsearch,xunsearch 什么关系

  • 关于SHFileOperation「建议收藏」

    关于SHFileOperation「建议收藏」
    CStringstr=”f://11″;
    FileOp.pFrom = (LPCTSTR)str;
     
    执行不成功,翻了下msdn
    ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.WIN32COM.v10.en/shellcc/platform/shell/reference/structures/shfileopstruct.htm
     pFromAddressofabuffertospecifyon

  • jdk1.8ArrayList主要方法和扩容机制(源码解析)

    jdk1.8ArrayList主要方法和扩容机制(源码解析)ArrayList简介:ArrayList实现了List接口它是一个可调整大小的数组可以用来存放各种形式的数据。并提供了包括CRUD在内的多种方法可以对数据进行操作但是它不是线程安全的,外ArrayList按照插入的顺序来存放数据。ArrayList的主要成员变量:privatestaticfinalintDEFAULT_CAPACITY=10;//数组默认初始容…

  • 对象字段java clone 中的浅复制和深复制

    对象字段java clone 中的浅复制和深复制

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号