mybatis的@MapperScan原理
第三方框架与Spring进行整合核心思想,是把其他框架所产生的对象放到Spring容器中,让其成为Bean。
像在spring中使用mybatis,开发人员只定义了mapper接口,而spring扫描候选component让其成为bean时,不会扫描接口。那如何让mapper接口变成bean,可以从容器中直接获取呢? 核心是利用FactoryBean接口的getObject()方法,返回一个mybatis的代理对象,并让它成为Bean放到spring容器中。 通过studentMmapper从容器中获取的是mybatis代理类的bean。
@Autowired
private StudentMapper studentMmapper;
- 在启动类或配置类上使用注解
@MapperScan("com.xxx.mapper")
指明mapper接口的包路径 - 在MapperScan.class中,通过@Import导入类MapperScannerRegistrar,该类实现了接口
ImportBeanDefinitionRegistrar
,在spring容器启动时,会调用MapperScannerRegistrar.registBeanDefinitions()
方法 - registBeanDefinitions()会创建一个
MapperScannerConfigurer
类型的beanDefinition 【为其设置属性,并注册到BeanDefinitionRegistry】 - MapperScannerConfigurer实现了接口
BeanDefinitionRegistryPostProcessor
,spring启动时,会执行==MapperScannerConfigurer.postProcessBeanDefinitionRegistry()==方法 - postProcessBeanDefinitionRegistry()方法会创建
ClassPathMapperScanner
扫描器,该类是mybatis提供的扫描器,继承自spring的ClassPathBeanDefinitionScanner。设置ClassPathMapperScanner能扫描到接口,因为spring的扫描器只会扫描类。 - ClassPathMapperScan改写
isCandidateComponent()
设置可以扫描到接口
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
spring的isCandidateComponent源码。该方法在类ClassPathScanningCandidateComponentProvider。ClassPathBeanDefinitionScanner继承了该类
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
- 使用spring扫描方法ClassPathBeanDefinitionScanner.doScan(),得到对应的beanDefinition
ClassPathMapperScanner.processBeanDefinitions()
对扫描出来的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean
,把AutowireMode修改为byType。因为扫描得到的BeanDefinition类型是接口类型,不符合要求。
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
-
扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean。
-
在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean
-
sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
-
MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
-
如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
-
而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象
注册BeanDefinition的方式
//在启动类中注册
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(WjFactoryBean.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(MemberMapper.class);
ac.registerBeanDefinition("memberMapper", beanDefinition);
实现接口BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(MyFactoryBean.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(MemberMapper.class);
beanDefinitionRegistry.registerBeanDefinition("memberMapper", beanDefinition);
AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition1.setBeanClass(MyFactoryBean.class);
beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(StudentMapper.class);
beanDefinitionRegistry.registerBeanDefinition("studentMapper", beanDefinition1);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
实现ImportBeanDefinitionRegistrar.registBeanDefinitions(),需要通过@Import注解导入创建的类。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(WjMapperScan.class.getName());
String path = (String)annotationAttributes.get("value");
MyBeanDefinitionScanner scanner = new MyBeanDefinitionScanner(registry);
scanner.addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
scanner.scan(path);
}
}
public class MyBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
public MyBeanDefinitionScanner (BeanDefinitionRegistry registry) {
super(registry);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface();
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
for (BeanDefinitionHolder beanDefinitionHolder: beanDefinitionHolders) {
BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
// 以下两行代码顺序不可调换
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
beanDefinition.setBeanClassName(MyFactoryBean.class.getName());
}
return beanDefinitionHolders;
}
}
BeanDefinitionRegistry就是spring容器。 它是个接口,DefaultListableBeanFactory实现了该接口。
参考:https://www.yuque.com/renyong-jmovm/spring/hzw30g