Spring重复扫描导致事务失败的解决方案及深入分析

本文解决了一个关于Spring和MyBatis环境下事务管理配置不当导致的问题。由于Spring的两个配置文件重复扫描了@Service注解,导致cglib生成的事务代理类失效。通过调整扫描范围,避免了重复扫描,实现了事务的正常运作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


问题及日志

使用Spring和mybatis,然后配置事务,出现SqlSession was not registered for synchronization because synchronization is not active,事务没有启用成功。

[org.mybatis.spring.SqlSessionUtils] - Creating a new SqlSession 
[org.mybatis.spring.SqlSessionUtils] - SqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@1a714e6e] was not registered for synchronization because synchronization is not active 
[org.springframework.jdbc.datasource.DataSourceUtils] - Fetching JDBC Connection from DataSource 
[org.mybatis.spring.transaction.SpringManagedTransaction] - JDBC Connection [jdbc: mysql://localhost:3306/iot, UserName=root@localhost, MySQL Connector Java]* will not be managed by Spring*
[com.haoyifen.mySSMTemplate.dao.UserMapper.selectByPrimaryKey] - ==> Preparing: select ID, USER_ID, USER_NAME, SEX, ADDRESS, PHONE_NUMBER, MAIL_BOX from user where ID = ? 
[com.haoyifen.mySSMTemplate.dao.UserMapper.selectByPrimaryKey] - ==> Parameters: 1(Integer) 
[com.haoyifen.mySSMTemplate.dao.UserMapper.selectByPrimaryKey] - <== Total: 1 
[org.mybatis.spring.SqlSessionUtils] - Closing non transactional SqlSession[org.apache.ibatis.session.defaults.DefaultSqlSession@1a714e6e]

IntelliJ IDEA的编辑器显示我的拦截路径是正确的。为UserService类创建事务配置。 
这里写图片描述

问题分析及解决方案

后来深入分析才发现,原来是扫描配置错误。 
我把Spring的ApplicationContext和DispatcherServlet(WebApplicationContext)配置使用两个文件分离开的,分别为spring.xml和spring-mvc.xml。在spring.xml中配置了事务。在两个文件中都启用了扫描设置,用于扫描@Service,@Component和@Controller

spring.xml
<!-- 自动扫描 -->
<context:component-scan base-package="com.haoyifen.mySSMTemplate"></context:component-scan>
  • 1
  • 2
  • 3
spring-mvc.xml
<context:component-scan base-package="com.haoyifen.mySSMTemplate"></context:component-scan>
  • 1
  • 2
  • 3

以上的配置使得spring-mvc重复扫描了userService类,而spring如果要使事务生效,就需要cglib为userService生成代理子类,在spring.xml中已经生成了代理类,而在spring-mvc.xml中,又重新扫描了一遍,使得原先cglib生成的代理子类失效,从而事务拦截也失效。所以我们应该设置spring-mvc.xml,使其不扫描@Service和@Component,spring.xml不扫描@Controller。配置如下:

spring.xml
<!-- 自动扫描 ,忽略@Controller注解的类-->
<context:component-scan base-package="com.haoyifen.mySSMTemplate">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
  • 1
  • 2
  • 3
  • 4
  • 5
spring-mvc.xml
<!--自动扫描该包,使spring-mvc只扫描controller包中的类(其中只有@Controller控制器),不会重复扫描到@Service或者@Component-->
<context:component-scan base-package="com.haoyifen.mySSMTemplate.controller">
</context:component-scan>
  • 1
  • 2
  • 3
  • 4

验证

修改后,再查看日志,就发现已经可以正确启用事务了。 
[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Acquired Connection [jdbc:mysql://localhost:3306/iot, UserName=root@localhost, MySQL Connector Java] for JDBC transaction 
[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Switching JDBC Connection[jdbc:mysql://localhost:3306/iot, UserName=root@localhost, MySQL Connector Java]* to manual commit* 
[org.mybatis.spring.SqlSessionUtils] - Creating a new SqlSession 
[org.mybatis.spring.SqlSessionUtils] -* Registering transaction synchronization for SqlSession*[org.apache.ibatis.session.defaults.DefaultSqlSession@1031238e] 
[org.mybatis.spring.transaction.SpringManagedTransaction] - JDBC Connection[jdbc:mysql://localhost:3306/iot, UserName=root@localhost, MySQL Connector Java]* will be managed by Spring* 
…. 
[org.mybatis.spring.SqlSessionUtils] -* Transaction synchronization closing SqlSession*[org.apache.ibatis.session.defaults.DefaultSqlSession@1031238e] 
[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction commit 
[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/iot, UserName=root@localhost, MySQL Connector Java] 
[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Releasing JDBC Connection[jdbc:mysql://localhost:3306/iot, UserName=root@localhost, MySQL Connector Java] after transaction 
[org.springframework.jdbc.datasource.DataSourceUtils] - Returning JDBC Connection to DataSource

深入日志分析

我们可以查看日志,来分析一下userService的创建过程。在修改xml文件之前:

Application初始化

创建Application时创建了一次userService,并使用cglib代理子类,生成了事务类: 
[org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring.xml] 
….. 
开始创建userService类 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] -* Creating shared instance of singleton bean ‘userService’* 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] - Creating instance of bean ‘userService’ 
…… 
缓存userService用于其他bean的注入 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] - Eagerly caching bean ‘userService’ to allow for resolving potential circular references 
…… 
注入userMapper 
[org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor] - Autowiring by type from bean name ‘userService’ to bean named ‘userMapper’ 
…… 
生成cglib代理类 
[org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator] - Creating implicit proxy for bean ‘userService’ with 0 common interceptors and 2 specific interceptors 
[org.springframework.aop.framework.CglibAopProxy] - Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.haoyifen.mySSMTemplate.service.UserService@2c06eb2b] 
….. 
完成创建,此时的userService已经是被cglib代理过的子类的实例 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] - Finished creating instance of bean ‘userService’

创建WebApplication子容器

接下来创建WebApplication子容器时,又创建了一次userService,并且没有使用cglib进行代理,所以事务失效。 
创建WebApplication 
[org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring-mvc.xml] 
开始创建userService类 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] -* Creating shared instance of singleton bean ‘userService’* 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] - Creating instance of bean ‘userService’ 
[org.springframework.beans.factory.annotation.InjectionMetadata] - Registered injected element on class [com.haoyifen.mySSMTemplate.service.UserService]: AutowiredFieldElement for private com.haoyifen.mySSMTemplate.dao.UserMapper com.haoyifen.mySSMTemplate.service.UserService.userMapper 
缓存userService用于其他bean的注入 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] - Eagerly caching bean ‘userService’ to allow for resolving potential circular references 
[org.springframework.beans.factory.annotation.InjectionMetadata] - Processing injected element of bean ‘userService’: AutowiredFieldElement for private com.haoyifen.mySSMTemplate.dao.UserMapper com.haoyifen.mySSMTemplate.service.UserService.userMapper 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean ‘userMapper’ 
注入userMapper 
[org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor] - Autowiring by type from bean name ‘userService’ to bean named ‘userMapper’ 
完成创建,本来由cglib代理的子类实例被替换成原生的userService实例,自然也就没有了事务功能 
[org.springframework.beans.factory.support.DefaultListableBeanFactory] -* Finished creating instance of bean ‘userService’*

我们按照上面的方法将spring-mvc.xml文件更改后,在创建WebApplication时,就不会再重新创建userService了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值