MyBatis-Spring整合深度解析:@MapperScan原理与SqlSessionTemplate线程安全机制
「MyBatis-Spring整合内核揭秘:@MapperScan自动注册原理+SqlSessionTemplate线程安全实现+完整工程搭建指南」
Spring与MyBatis整合的架构设计哲学
在企业级应用开发中,Spring框架的IoC容器和事务管理能力与MyBatis的SQL映射能力形成了完美的互补。MyBatis-Spring整合项目的核心目标就是让这两个优秀的框架能够无缝协作,发挥各自的优势。这种整合不仅仅是简单的API桥接,更是一种深度的架构融合。
目录
MyBatis-Spring整合深度解析:@MapperScan原理与SqlSessionTemplate线程安全机制
ClassPathMapperScanner的自定义扫描逻辑
SqlSessionInterceptor:线程安全的守护者
🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)
🔥🔥🔥 有兴趣可以联系我。文末有免费源码
免费获取源码。
更多内容敬请期待。如有需要可以联系作者免费送
更多源码定制,项目修改,项目二开可以联系作者
点击可以进行搜索(每人免费送一套代码):千套源码目录(点我)
2025元旦源码免费送(点我)
我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。
基础工程搭建:模块化设计的艺术
项目结构的最佳实践
一个良好的MyBatis-Spring整合项目应该遵循清晰的分层架构:
XML
mybatis-spring-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── config/ # 配置类
│ │ │ ├── entity/ # 实体类
│ │ │ ├── mapper/ # Mapper接口
│ │ │ ├── service/ # 业务服务层
│ │ │ └── Application.java # 启动类
│ │ └── resources/
│ │ ├── mapper/ # MyBatis映射文件
│ │ ├── application.yml # 应用配置
│ │ └── mybatis-config.xml # MyBatis配置
│ └── test/ # 测试代码
├── pom.xml # Maven依赖配置
└── README.md
核心依赖的精准配置
在pom.xml中,我们需要精心配置Spring和MyBatis的相关依赖:
XML
<dependencies>
<!-- Spring Context 核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<!-- MyBatis Spring 整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!-- MyBatis 核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
</dependencies>
@MapperScan注解的自动注册原理深度解析
注解的元数据定义
@MapperScan
注解是MyBatis-Spring整合的核心入口,它通过Spring的组件扫描机制自动注册Mapper接口:
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
// 指定要扫描的包路径
String[] value() default {};
// 指定要扫描的包名
String[] basePackages() default {};
// 指定基础包类
Class<?>[] basePackageClasses() default {};
// 指定Bean名称生成器
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
// 指定注解过滤器
Class<? extends Annotation> annotationClass() default Annotation.class;
// 指定标记接口
Class<?> markerInterface() default Class.class;
// SQL会话模板引用
String sqlSessionTemplateRef() default "";
// SQL会话工厂引用
String sqlSessionFactoryRef() default "";
// Mapper工厂Bean类
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
MapperScannerRegistrar的注册机制
@MapperScan
通过@Import
注解引入了MapperScannerRegistrar
,这是Spring框架中处理@Import
注解的标准扩展点:
java
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 获取@MapperScan注解的属性
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
// 注册Mapper扫描器
registerBeanDefinitions(mapperScanAttrs, registry);
}
}
private void registerBeanDefinitions(AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry) {
// 创建ClassPathMapperScanner
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 配置扫描器参数
scanner.setAnnotationClass(annoAttrs.getClass("annotationClass"));
scanner.setMarkerInterface(annoAttrs.getClass("markerInterface"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setMapperFactoryBeanClass(annoAttrs.getClass("factoryBean"));
// 注册过滤器
scanner.registerFilters();
// 执行扫描
scanner.scan(annoAttrs.getStringArray("value"));
scanner.scan(annoAttrs.getStringArray("basePackages"));
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
ClassPathMapperScanner的自定义扫描逻辑
ClassPathMapperScanner
继承自Spring的ClassPathBeanDefinitionScanner
,重写了扫描逻辑以适配MyBatis的特殊需求:
java
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
private String sqlSessionFactoryBeanName;
private String sqlSessionTemplateBeanName;
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用父类扫描方法获取候选Bean定义
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" +
Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
// 处理扫描到的Bean定义
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 设置Mapper工厂Bean类
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBeanClass);
// 设置SQL会话工厂引用
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
}
// 设置SQL会话模板引用
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
}
}
}
}
MapperFactoryBean的代理创建机制
MapperFactoryBean
是Spring FactoryBean的实现,负责创建Mapper接口的代理实例:
java
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;
}
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
// 验证Mapper接口配置
if (this.mapperInterface == null) {
throw new IllegalArgumentException("Property 'mapperInterface' is required");
}
// 将Mapper接口注册到MyBatis配置中
if (this.addToConfig && !getSqlSession().getConfiguration().hasMapper(this.mapperInterface)) {
try {
getSqlSession().getConfiguration().addMapper(this.mapperInterface);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}
@Override
public T getObject() throws Exception {
// 从SQL会话中获取Mapper代理实例
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
@Override
public boolean isSingleton() {
return true;
}
}
SqlSessionTemplate的线程安全实现机制
线程安全的设计挑战
在传统的MyBatis使用中,SqlSession
实例不是线程安全的,这意味着在Web应用等多线程环境中,每个请求都需要创建新的SqlSession
实例。SqlSessionTemplate
通过巧妙的包装设计解决了这个问题。
SqlSessionTemplate的核心架构
java
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType,
new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment(), true));
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
// 创建动态代理,这是线程安全的关键
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
}
SqlSessionInterceptor:线程安全的守护者
SqlSessionInterceptor
是SqlSessionTemplate
实现线程安全的核心,它通过动态代理拦截所有方法调用:
java
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取当前线程的SqlSession
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
// 执行目标方法
Object result = method.invoke(sqlSession, args);
// 如果当前没有事务,立即提交
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
// 异常转换:将MyBatis异常转换为Spring的DataAccessException
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null &&
unwrapped instanceof PersistenceException) {
throw SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
} else {
throw unwrapped;
}
} finally {
// 关闭SqlSession(如果不在事务中)
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
SqlSessionUtils的会话管理
SqlSessionUtils
负责SqlSession
与Spring事务的集成管理:
java
public abstract class SqlSessionUtils {
private static final String NO_EXECUTOR_TYPE_SPECIFIED = "No ExecutorType specified";
private static final String NO_SQL_SESSION_FACTORY_SPECIFIED = "No SqlSessionFactory specified";
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory,
ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
// 参数验证
if (sessionFactory == null) {
throw new IllegalArgumentException(NO_SQL_SESSION_FACTORY_SPECIFIED);
}
// 尝试从当前事务中获取SqlSession
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager
.getResource(sessionFactory);
if (holder != null && holder.isSynchronizedWithTransaction()) {
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException(
"Cannot change ExecutorType when there is an existing transaction");
}
// 增加引用计数
holder.requested();
return holder.getSqlSession();
}
// 创建新的SqlSession
SqlSession session = sessionFactory.openSession(executorType);
// 如果当前有事务,注册到事务同步管理器
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 创建SqlSessionHolder
SqlSessionHolder holderToRegister = new SqlSessionHolder(session, executorType, exceptionTranslator);
TransactionSynchronizationManager.bindResource(sessionFactory, holderToRegister);
// 注册事务同步
holderToRegister.setSynchronizedWithTransaction(true);
if (holderToRegister.isSynchronizedWithTransaction()) {
holderToRegister.requested();
}
}
return session;
}
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
// 如果session为null或已关闭,直接返回
if (session == null || session instanceof SqlSessionTemplate) {
return;
}
// 检查session是否与当前事务绑定
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager
.getResource(sessionFactory);
if (holder != null && holder.getSqlSession() == session) {
// 减少引用计数,但不立即关闭
holder.released();
} else {
// 不在事务中,立即关闭session
session.close();
}
}
}
事务集成的工作原理
SqlSessionTemplate
与Spring事务管理的集成是通过TransactionSynchronizationManager
实现的:
java
public abstract class TransactionSynchronizationManager {
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");
}
完整配置示例与最佳实践
Java配置方式
java
@Configuration
@MapperScan(basePackages = "com.example.mapper",
sqlSessionFactoryRef = "sqlSessionFactory")
public class MyBatisConfig {
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/*.xml"));
return sessionFactory.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory());
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
事务管理的配置
java
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public TransactionInterceptor transactionInterceptor(PlatformTransactionManager transactionManager) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionManager(transactionManager);
// 配置事务属性
Properties properties = new Properties();
properties.setProperty("get*", "PROPAGATION_REQUIRED,readOnly");
properties.setProperty("find*", "PROPAGATION_REQUIRED,readOnly");
properties.setProperty("select*", "PROPAGATION_REQUIRED,readOnly");
properties.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");
properties.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
properties.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
interceptor.setTransactionAttributes(properties);
return interceptor;
}
}
性能优化与问题排查
连接池配置优化
java
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
config.setUsername("root");
config.setPassword("password");
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
config.setAutoCommit(false);
return new HikariDataSource(config);
}
常见问题排查指南
-
Mapper接口无法注入 :检查
@MapperScan
包路径配置 -
事务不生效 :确认
@EnableTransactionManagement
已启用 -
连接泄漏 :检查
SqlSession
是否正确关闭 -
性能问题:调整连接池参数和MyBatis缓存配置
总结
MyBatis-Spring整合通过精妙的架构设计,实现了两个框架的无缝协作:
-
@MapperScan机制:通过Spring的扩展点实现Mapper接口的自动注册
-
SqlSessionTemplate:通过动态代理和线程局部存储实现线程安全
-
事务集成 :通过
TransactionSynchronizationManager
实现与Spring事务的深度集成
理解这些底层机制,不仅有助于我们更好地使用MyBatis-Spring整合,还能在遇到复杂问题时快速定位根本原因,这是构建高质量企业级应用的重要基础。

🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)
🔥🔥🔥 有兴趣可以联系我。文末有免费源码
💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!💖常来我家多看看,
📕网址:扣棣编程** ,
🎉感谢支持常陪伴,
🔥点赞关注别忘记!**💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!
往期文章推荐:
基于Springboot + vue实现的学生宿舍信息管理系统
免费获取宠物商城源码--SpringBoot+Vue宠物商城网站系统
【2025小年源码免费送】