大家好,又见面了,我是你们的朋友全栈君。
@MapperScan
代码语言:javascript复制@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
... }
@Import 的作用就是向spring容器中导入一个BeanDefinition对象
MapperScannerRegistrar
代码语言:javascript复制public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
... }
通过源码看出这个导入的类还是一个ImportBeanDefinitionRegistrar,这个接口下面有一个registerBeanDefinitions(…) 通过这个方法的 BeanDefinitionRegistry ,就可以完成BeanDefinition 的注册
代码语言:javascript复制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));
}
- 获取MapperScan注解上的一些信息
- 定义一个扫描器 ClassPathMapperScanner ,这个扫描器其实是继承自spring的ClassPathBeanDefinitionScanner,但是在实例化的时后 第二个参数值为false , super(registry, false); 意思是说不适用spring提供的默认的过滤方式 对于spring的扫描器而言,需要满足两个条件才会被扫描,首先不能是接口,其次是不需要标注@Component注解,所以这里想要扫描 mapper 的接口就需要自己去实现过滤的规则,到那时这里的annotationClass 只是记录一下,具体的操作在registerFilters() 3.注册Filters 4.进行doScan 扫描
还有一点需要注意的是,spring在进行扫描的时候,会去调用一个方法isCandidateComponent(),这个方法在spring的scanner中的实现如下
代码语言:javascript复制 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的扫描器重写了这个方法
代码语言:javascript复制 @Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
也就是@MapperScan 中的扫描器只会扫描接口
接下来思考一个问题,通过上述方式的确是能让spring帮助mybatis进行扫描,但是扫描出来的BeanClass是什么呢??? 是mapper接口??? 显然不能是这些,接口是无法直接进行实例化的,所以这些扫描出来的BeanDefinition还需要进行处理 处理的逻辑就在doScan()==>processBeanDefinitions()
代码语言:javascript复制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是什么呢?
代码语言:javascript复制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()
代码语言:javascript复制 //当前的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);
}
}
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/144735.html原文链接:https://javaforall.cn