spring与mybatis的整合源码解析
根据Spring的工作原理,许多框架与Spring的整合本质上是将其他框架创建的对象纳入Spring的管理之下,使其成为Spring容器中的Bean。这样做的目的是为了让这些对象能够享受Spring框架提供的依赖注入、生命周期管理以及事务管理等功能。
以MyBatis为例,MyBatis可以作为一个独立的框架使用,这意味着你需要手动创建一些核心对象,如SqlSessionFactory和SqlSession,并通过它们来执行SQL操作。但是,这种方式缺乏Spring所提供的便捷性和灵活性。
为了将MyBatis与Spring进行整合,我们需要将MyBatis的关键组件,如SqlSessionFactory,注册为Spring容器中的Bean。一旦这些组件成为Spring管理的Bean,就可以通过依赖注入的方式方便地获取和使用它们。这意味着在Spring项目中,开发者可以直接在Service层注入MyBatis的Mapper接口或SqlSessionTemplate等组件,而不需要关心这些对象是如何创建和管理的。
整合后的效果是,开发者可以更加专注于业务逻辑的编写,而Spring和MyBatis则共同承担了数据访问层的细节工作,提高了代码的可维护性和扩展性。此外,Spring的事务管理功能也可以无缝地应用于MyBatis的操作中,确保数据的一致性和完整性。
1 导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.2</version>
</dependency>
</dependencies>
2 @MapperScan作用
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//额外引入一个注册类 MapperScannerRegistrar
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
String lazyInitialization() default "";
String defaultScope() default "";
boolean processPropertyPlaceHolders() default true;
}
//1 实现了ImportBeanDefinitionRegistrar接口 可以向spring容器注册bean定义 在bean工厂后处理器执行阶段会执行
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
public MapperScannerRegistrar() {
}
/** @deprecated */
@Deprecated
public void setResourceLoader(ResourceLoader resourceLoader) {
}
//2 注册bean定义
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//3 获取注解信息 主要是mapperscan的注解信息
AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
this.registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
//4 注册MapperScannerConfigurer 这个类会记录mapperscan的一下信息
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", annoAttrs.getBoolean("processPropertyPlaceHolders"));
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList();
basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll((Collection)Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
if (basePackages.isEmpty()) {
//5 获取mapper的扫描路径
basePackages.add(getDefaultBasePackage(annoMeta));
}
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
String defaultScope = annoAttrs.getString("defaultScope");
if (!"".equals(defaultScope)) {
builder.addPropertyValue("defaultScope", defaultScope);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
builder.setRole(2);
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
return importingClassMetadata.getClassName() + "#" + MapperScannerRegistrar.class.getSimpleName() + "#" + index;
}
private static String getDefaultBasePackage(AnnotationMetadata importingClassMetadata) {
return ClassUtils.getPackageName(importingClassMetadata.getClassName());
}
static class RepeatingRegistrar extends MapperScannerRegistrar {
RepeatingRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScansAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
if (mapperScansAttrs != null) {
AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value");
for(int i = 0; i < annotations.length; ++i) {
this.registerBeanDefinitions(importingClassMetadata, annotations[i], registry, MapperScannerRegistrar.generateBaseBeanName(importingClassMetadata, i));
}
}
}
}
}
//实现了BeanDefinitionRegistryPostProcessor接口 可以注册bean定义
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();
}
//1 创建ClassPathMapperScanner 这个对象重写可spring的扫描对象规则 可以扫描接口
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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(this.lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
}
if (StringUtils.hasText(this.defaultScope)) {
scanner.setDefaultScope(this.defaultScope);
}
//2 支持接口的扫描 对spring的默认扫描规则新增了接口扫描的扩展
scanner.registerFilters();
//3 调用扫描方法
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
}
总结:
-
首先在@mapperscan注解配置属性 其中最重要的是basePackages 同时会引入一个核心类-MapperScannerRegistrar
-
MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar 在spring启动阶段可以向bean工厂注册bean定义 在registerBeanDefinitions方法中主要封装了@mapperscan注解的属性 封装成MapperScannerConfigurer对象 注册到bean工厂中
-
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口 在spring启动阶段会想bean工厂注册bean定义 在postProcessBeanDefinitionRegistry方法中 创建了ClassPathMapperScanner对象 这个对象继承了spring的扫描器 重写了spring的过滤规则以及扫描后置处理方法 支持扫描接口对象
3 扫描流程
核心类:ClassPathMapperScanner
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//1 扫描所有接口并封装成bean定义
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> {
return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
});
} else {
//2 由于接口不能实例化 需要将bean定义修改
//这里mybatis提供了一个实现factorybean接口的bean 用来创建接口的代理对象
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
BeanDefinitionRegistry registry = this.getRegistry();
Iterator var4 = beanDefinitions.iterator();
while(var4.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next();
AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
boolean scopedProxy = false;
if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> {
return new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]");
});
scopedProxy = true;
}
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> {
return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface";
});
//1 添加构造方法参数 MapperFactoryBean的构造方法需要传入接口对象
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
try {
definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));
} catch (ClassNotFoundException var10) {
}
//2 将bean类型修改为MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
definition.setAttribute("factoryBeanObjectType", beanClassName);
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(() -> {
return "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(() -> {
return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.";
});
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> {
return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.";
});
definition.setAutowireMode(2);
}
definition.setLazyInit(this.lazyInitialization);
if (!scopedProxy) {
if ("singleton".equals(definition.getScope()) && this.defaultScope != null) {
definition.setScope(this.defaultScope);
}
if (!definition.isSingleton()) {
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
registry.removeBeanDefinition(proxyHolder.getBeanName());
}
//3 注册封装好的bean方法
registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
}
}
}
}
4 MapperFactoryBean
//将mybatis提供的代理对象注入到spring容器中
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
}
//代理的接口类
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
protected void checkDaoConfig() {
super.checkDaoConfig();
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = this.getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception var6) {
this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
throw new IllegalArgumentException(var6);
} finally {
ErrorContext.instance().reset();
}
}
}
public T getObject() throws Exception {
//获取mybatis的代理对象
return this.getSqlSession().getMapper(this.mapperInterface);
}
public Class<T> getObjectType() {
return this.mapperInterface;
}
public boolean isSingleton() {
return true;
}
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
}
public boolean isAddToConfig() {
return this.addToConfig;
}
}
5 总结
为了清晰地描述Spring整合MyBatis时如何将Mapper接口注册为Bean的过程,我们可以按以下步骤重新梳理:
-
通过
@MapperScan注解导入MapperScannerRegistrar类。这个类负责在Spring启动时注册Mapper接口。 -
MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,因此在Spring启动期间,Spring会调用该类中的registerBeanDefinitions方法。 -
在
registerBeanDefinitions方法中,定义了一个ClassPathMapperScanner对象,用于扫描指定包下的Mapper接口。 -
ClassPathMapperScanner对象配置为可以扫描接口。由于Spring默认不会扫描接口,这里需要明确指出扫描规则。 -
ClassPathMapperScanner类重写了isCandidateComponent方法,使得只有接口被认为是候选Component。 -
通过Spring的扫描机制,将接口扫描出来并获取对应的
BeanDefinition。 -
对扫描得到的
BeanDefinition进行修改,将BeanClass修改为MapperFactoryBean,并将AutowireMode设置为BY_TYPE。 -
扫描完成后,Spring将基于修改后的
BeanDefinition创建Bean,即为每个Mapper创建一个对应的FactoryBean。 -
在
MapperFactoryBean的getObject方法中,通过调用getSqlSession()方法获取SqlSession对象,并根据对应的Mapper接口生成一个Mapper代理对象。这个代理对象最终成为Spring容器中的Bean。 -
SqlSession对象是由MyBatis提供的,它的创建依赖于SqlSessionFactory。
总结来说,通过@MapperScan注解,Spring在启动期间会识别并注册Mapper接口为Bean。这个过程涉及到了MapperScannerRegistrar类的使用,以及ClassPathMapperScanner对象的配置和扫描规则的设定。最终,通过MapperFactoryBean创建的Mapper代理对象成为Spring容器的一部分,可以像其他Bean一样被注入和使用。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)