【mybatis系列】spring是如何整合mybatis将Mapper接口注册为Bean的
简单来说:
Spring整合MyBatis将Mapper接口注册为Bean的原理,主要是通过MapperScannerConfigurer (实现了BeanDefinitionRegistryPostProcessor接口)(或Spring Boot的自动配置)扫描Mapper接口,创建MapperFactoryBean的BeanDefinition实例,并注册到Spring容器中。MapperFactoryBean通过JDK动态代理为Mapper接口生成代理对象,从而实现了Mapper接口的Bean化。这一过程简化了开发者对数据库操作的管理,提高了开发效率。
参考如下流程:
1. 首先mybatis的mapper接口核心是 JDK动态代理
2. spring扫描类的时候会排除接口和抽象类,这样接口和抽象类是无法注册到IOC容器中
3. mybatis实现了BeanDefinitionRegistryPostProcessor 可以动态注册BeanDefinition
4. 需要自定义扫描器(继承spring内部扫描器 ClassPathBeanDefinitionScanner)重写排除接口的方法(isCandidateComponent)
5. 以上,就完成了接口注册成了BeanDefinition,但是无法实例化Bean,因为接口无法实例化
6 需要将BeanDefinition的BeanClass替换成JDK动态代理的实例对象(偷天换日)
7. mybatis通过FactoryBean的工厂方法设计模式,可以自由控制Bean的实例化过程
spring集成mybatis,核心点:JDK动态代理,BeanDefinitionRegistryPostProcessor, FactoryBean。
Spring整合MyBatis将Mapper接口注册为Bean的原理,主要涉及到Spring的Bean注册机制以及MyBatis的Mapper接口处理。这一过程可以分为以下几个关键步骤:
1. 配置数据源和SqlSessionFactory
- 在Spring的配置文件中(可以是XML配置、Java配置或注解配置),首先需要配置数据源(DataSource),该数据源会被注入到SqlSessionFactory中。
- 使用SqlSessionFactoryBean来创建SqlSessionFactory实例。SqlSessionFactoryBean实现了FactoryBean接口,能够生成SqlSessionFactory对象。这个过程中,会配置MyBatis的配置文件(如mybatis-config.xml)以及Mapper映射文件等信息。
2. MapperScannerConfigurer扫描Mapper接口
- MapperScannerConfigurer是Spring和MyBatis整合过程中一个重要的类,它实现了BeanDefinitionRegistryPostProcessor接口。Spring容器启动时会回调其postProcessBeanDefinitionRegistry方法。
- MapperScannerConfigurer负责扫描指定包路径下的所有Mapper接口。对于每一个扫描到的Mapper接口,MapperScannerConfigurer会创建一个MapperFactoryBean的BeanDefinition实例,并将其注册到Spring容器中。这里,MapperFactoryBean的beanClass属性被设置为Mapper接口对应的类(但实际上是通过JDK动态代理来实现的)。
3. MapperFactoryBean生成代理对象
- MapperFactoryBean是FactoryBean的一个实现,当Spring容器需要获取Mapper接口的Bean时,会调用MapperFactoryBean的getObject()方法。
- getObject()方法内部会通过SqlSession的getMapper()方法,基于Mapper接口创建一个JDK动态代理对象。这个代理对象会拦截对Mapper接口方法的调用,并将这些调用转发给MyBatis执行。
4. @MapperScan注解的自动化配置(适用于Spring Boot)
- 在Spring Boot项目中,通常不需要手动配置MapperScannerConfigurer,因为Spring Boot提供了自动配置。
- 可以通过在配置类上使用@MapperScan注解来指定Mapper接口所在的包路径,Spring Boot会自动为这些Mapper接口创建Bean,并注册到Spring容器中。
- 如果未使用@MapperScan注解,并且Mapper接口所在的包不在@ComponentScan的扫描范围内,则需要在Mapper接口上使用@Mapper注解来显式指定该接口是一个Mapper接口,以便Spring Boot能够自动识别并注册。
MapperScannerConfigurer.java
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
...
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
scanner.setMapperHelper(this.mapperHelper);
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
...
}
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
Iterator var3 = beanDefinitions.iterator();
while(var3.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();
GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBean.getClass());
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) {
this.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) {
this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(2);
}
}
}
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
}