Spring重复扫描导致事务失败的解决方案及深入分析
问题及日志
使用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了。
Spring重复扫描导致事务失败的解决方案及深入分析的更多相关文章
- 解决spring、springMVC重复扫描导致事务失效的问题
在主容器中(applicationContext.xml),将Controller的注解排除掉 1 2 3 而在springMVC配置文件中将Service注解给去掉 1 2 3 4 因为spring ...
- Spring配置文件xsi:schemaLocation无法解析导致启动失败的解决方案
今天遇到过情况,spring的配置文件在本地读取没有问题,扔到线上服务器运行就报无法解析xml,找了很久问题,发现是因为线上服务器无法上网,导致无法下载相关的xsd文件,没办法不能上网就只有使用本地的 ...
- @Transactional spring事务无效的解决方案
关于@Transactional注解 一般都认为要注意以下三点 1 .在需要事务管理的地方加@Transactional 注解.@Transactional 注解可以被应用于接口定义和接口方法.类定义 ...
- idea创建springboot工程,总出现响应超时问题,或者无法连接http://start.spring.io导致创建失败
问题描述如下: idea创建springboot工程,总出现响应超时问题,或者无法连接http://start.spring.io导致创建失败 从我出现此类问题几次的解决方案 依照解决效率分为一下三种 ...
- [spring transaction],service实现类中非事务方法直接调用自身事务方法导致事务无效的原因
首先,准备service接口,两个 public interface AccountService { public void createAccount(Account account, int t ...
- Spring基于AOP的事务管理
Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...
- Spring之声明式事务
在讲声明式事务之前,先回顾一下基本的编程式事务 编程式事务: //1.获取Connection对象 Connection conn = JDBCUtils.getConnection(); try { ...
- Spring的声明式事务管理
在service类前加上@Transactional,声明这个service所有方法需要事务管理.每一个业务方法开始时都会打开一个事务. Spring默认情况下会对运行期例外(RunTimeExcep ...
- RabbitMQ 消息顺序、消息幂等、消息重复、消息事务、集群
1. 消息顺序 场景:比如下单操作,下单成功之后,会发布创建订单和扣减库存消息,但扣减库存消息执行会先于创建订单消息,也就说前者执行成功之后,才能执行后者. 不保证完全按照顺序消费,在 MQ 层面支持 ...
随机推荐
- map经典代码---java基础
package com.mon11.day6; import java.util.HashMap; import java.util.Map; /** * 类说明 :实现英文简称和中文全名之间的键值对 ...
- Codeforces Round #477 (rated, Div. 2, based on VK Cup 2018 Round 3) E 贪心
http://codeforces.com/contest/967/problem/E 题目大意: 给你一个数组a,a的长度为n 定义:b(i) = a(1)^a(2)^......^a(i), 问, ...
- elementUI 通用确认框
Util.vue <script> import VueResource from 'vue-resource' function confirm(_this, operate, fun) ...
- Linux iptables常用命令的使用
为什么会有本文 因为最近帮一个朋友布署一个上网梯子,他那边本来用的是v2ray,但是他想用ssr,但是安装配置ssr过程中出了很多问题,比如linux内核版本4.9有点老,不支持bbr加速.无法连接s ...
- python2.7中MySQLdb的安装与使用详解
Python2.7中MySQLdb的使用 import MySQLdb #1.建立连接 connect = MySQLdb.connect( '127.0.0.1', #数据库地址 'root', # ...
- FTP主动/被动原理
FTP 主动模式 1.客户端用大于1024的高位端口发起初始化连接到vsftp服务器的21端口 2.vsftp服务器的21端口主动与客户端大于1024的高位端口建立控制连接 3.vsftp服务器的20 ...
- centos6.8安装mysql5.6【转】
首先先要去看看本机有没有默认的mysql, 本地默认有的,我们应先卸载,在安装新的这个逻辑. rpm -qa | grep mysql 我本机默认安装的mysql5.1.73 下一步删除 rpm -e ...
- 走进异步编程的世界--async/await项目使用实战
起因:今天要做一个定时器任务:五分钟查询一次数据库发现超时未支付的订单数据将其状态改为已经关闭(数据量大约100条的情况) 开始未使用异步: public void SelfCloseGpPayOrd ...
- Android网络框架之Retrofit + RxJava + OkHttp 变化的时代
1.什么是Retrofit框架? 它是Square公司开发的现在非常流行的网络框架,所以我们在导入它的包的时候都可以看到这个公司的名字,目前的版本是2. 特点: 性能好,处理快,使用简单,Retrof ...
- nginx前后端分离路由配置
参考链接: https://blog.csdn.net/qq_30021219/article/details/80901199