问题场景

项目使用的框架:Spring 4.1.4 + Hibernate 4.3.8 + MySQL。

web.xml中对Spring的配置:

<!-- 把 Spring 容器集成到 Web 应用里面 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener> <!-- spring配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/applicationContext.xml</param-value>
</context-param> <!--DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,
而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。-->
<!--DispatcherServlet会默认加载[servlet-name]-servlet.xml文件-->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

项目使用AOP声明式事务配置

<!--  声明式容器事务管理 ,transaction-manager指定事务管理器为transactionManager -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="query*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="save*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="update*" rollback-for="Exception" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--只对业务逻辑层实施事务 -->
<aop:pointcut expression="execution(* com.zhimajp.auction.service.impl..*.*(..))" id="busiLogicService"/>
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="busiLogicService"/>
</aop:config>

采用annotation方式声明Spring Bean。

遇到的问题是:通过Hibernate执行save方法后,数据未能插入到DB中并且控制台也没有打印出SQL(控制台没有输出)。

解决问题

通过仔细排查,阅读网络文章后,发现问题出现在spring-servlet.xml中:

<!--扫描注解包 配置这条便可移除 <context:annotation-config/> --> <context:component-scan base-package="com.zhimajp.auction" />

上述配置的结果是:SpringMVC对Service和Dao的所有package进行了扫描装载。

问题分析:

1、Spring与SpringMVC属于父子容器关系。框架启动时先启动Spring容器,而后启动SpringMVC容器。子容器可以访问父容器中的Bean,而父容器不能访问子容器中的Bean。

2、由于SpringMVC在扫描时扩大了扫描范围,装载了@Service标识的类的实例,从而导致Controller层在注入Service时,实际注入的时子容器中的Service实例。

3、事务被配置在父容器中,Spring父容器在装载Service时会同时应用事务配置,而SpringMVC只是单纯加载Service的实例。

问题解决:

将SpringMVC的包扫描限定在Controller。

<context:component-scan base-package="com.zhimajp.auction.controller" />

进一步

打印容器管理的bean名称

我们使SpringMVC扫描Controller和Service,Spring扫描Service和DAO。

使用以下代码打印父子窗口管理的bean名称:

     WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
String[] definationBeanNames = webApplicationContext.getBeanNamesForAnnotation(Service.class);
List<String> names = new ArrayList<String>(Arrays.asList(definationBeanNames));
Collections.addAll(names, webApplicationContext.getBeanNamesForAnnotation(Controller.class));
Collections.addAll(names, webApplicationContext.getBeanNamesForAnnotation(Repository.class));
System.out.println("Spring 父容器管理的Bean:");
for(String beanName : names){
System.out.println(beanName);
}
webApplicationContext = RequestContextUtils.getWebApplicationContext(request);
definationBeanNames = webApplicationContext.getBeanNamesForAnnotation(Service.class);
names = new ArrayList<String>(Arrays.asList(definationBeanNames));
Collections.addAll(names, webApplicationContext.getBeanNamesForAnnotation(Controller.class));
Collections.addAll(names, webApplicationContext.getBeanNamesForAnnotation(Repository.class)); System.out.println("SpringMVC 子容器管理的Bean:");
for(String beanName : names){
System.out.println(beanName);
}

我们发现父子容器同时维护了Service层的类的实例,并且应该是两个独立的实例。

只使用子容器,而完全不使用父容器

现在我们测试另外一个场景

将web.xml中,注释掉ContextLoaderListener,修改配置为:

<!--DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,
而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。-->
<!--DispatcherServlet会默认加载[servlet-name]-servlet.xml文件-->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/applicationContext.xml;/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

移除了父容器,所有配置文件全部交由SpringMVC加载 。

打印结果如下:

子容器管理所有的bean,而父容器为null(代码抛出了空指针)。

项目同样运行正常。

总结

1、当事务交由Spring管理时,Spring负责管理session中事务的开启、关闭、flush等步骤,开发者只需调用例如save、update方法即可。

2、当web项目框架中存在父子容器,且事务由父容器管理时,就应当注意SpringMVC对包的扫描范围并且只需扫描Controller组件。官方推荐:父子容器应当各执其责。

3、如果子容器加载了Service的话,则在该实例上事务并不会生效。也就是Spring不会在service的方法被调用时自动开启事务。

4、基于2中的前提:SpringMVC应只加载web相关配置(视图配置、Controller注解扫描),由Spring加载数据源、事务配置、Service和Dao注解扫描。

SpringMVC对包的扫描范围扩大后,导致的事务配置不生效问题的更多相关文章

  1. 将struts的jar包拷贝到WEB-INF/lib导致eclipse中配置好的javadoc失效

    我通过这个步骤导入了struts的jar包并且配置好了javadoc,且亲测可用: http://www.cnblogs.com/qrlozte/p/3173805.html 但是当我把struts的 ...

  2. springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法

    业务场景: A.B两个单位,系统部署同一套代码: A.B两系统能相互访问: 要求将数据从A系统同步到B系统,再将反馈信息回发给A: 实际开发情况: 因为系统比较小,最开始设计架构的时候没有考虑到消息互 ...

  3. 解决web项目存在多个log4j.properties配置文件,导致日志级别配置不生效问题

    java开启log4j的debug模式 -Dlog4j.debug=true tomcat启动debug模式: linux打开catalina.sh导入: export JAVA_OPTS=" ...

  4. 导致spring事务配置不起作用的一种原因

    @Component public class AnalyticsApplication { @Autowired private InitializationActionService initia ...

  5. Java SpringMvc Jar包下载及部署方式_转载

    本次来记录一下关于 springmvc jar包的下载以及部署 首先登陆   http://repo.spring.io/release/org/springframework/spring/到里面我 ...

  6. SpringMVC编程:初始化项目部署后,主页404代码问题解决

    SpringMVC 主页404问题 初始化项目后,Tomcat服务器显示404报错问题! 问题解决的原因是:项目依赖的jar包没有随着项目一块打包部署在Tomcat服务器上面,即缺少项目lib依赖. ...

  7. ueditor的工具栏显示乱码解决方法 小问题.. 是你的页面编码与语言包js编码不符所导致的

    ueditor的工具栏显示乱码解决方法 小问题..  是你的页面编码与语言包js编码不符所导致的解决方法:用记事本将ueditor\..\lang\zh-cn\zh-cn.js打开,然后保存为ANSI ...

  8. SQL Server数据库账号密码变更后导致vCenter Server无法访问数据库

    SQL Server数据库账号密码变更后导致vCenter Server无法访问数据库 1.1状况描述: 若SQL Server数据库的账号(这里以sa为例)密码发生了变更,那么连接数据的客户端vCe ...

  9. Centos7修改profile文件后导致vi command not find

    Centos7修改profile文件后导致vi command not find,原因是profile文件没有配置正确,系统就无法找到精确命令了.解决方法: 1.在命令行中输入:export PATH ...

随机推荐

  1. Python爬虫利器:Beautiful Soup

    Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.使用它来处理HTML页面就像JavaScript代码操作HTML DOM树一样方便.官方中文文档地址 1. 安 ...

  2. 【二十】mysqli基于面向过程与面向对象的编程

    面向过程的方式 musqli扩展库操作mysql数据库步骤: 1.获取连接并选择数据库 //语法 mysqli_connect(host,username,password,dbname,port,s ...

  3. POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

    一般预流推进算法: 算法思想: 对容量网络G 的一个预流f,如果存在活跃顶点,则说明该预流不是可行流. 预流推进算法就是要选择活跃顶点,并通过它把一定的流量推进到它的邻接顶点,尽可能将正的赢余减少为0 ...

  4. oracle触发器 调用 web接口

    最近要求开发当数据表发生变化的时候调用web接口的需求,上网找了好几篇文章看着都觉得不是很好,也根据别人的思路去实现了下,感觉都不太理想,最后使用URLConnection实现了调用.具体查看一下代码 ...

  5. Yii2如何添加sql日志记录的配置信息

    在使用Yii2框架的时候,常常会出现没有sql日志记录的问题.在代码里一句一句的打印sql语句也不现实.所以就要用文件记录起来. 在 config/web.php 里面的 log配置中增加如下配置 [ ...

  6. PHP开发b2c商城价格

    电商的快速发展不断地挤压传统企业的生存空间,渠道越来越窄,所以现在很多企业开始往线上发展,搭建自己的B2C商城,直接面向消费者进行销售.那开发b2c商城价格怎么样?很多企业都是比较关心到商城价格这个问 ...

  7. HTTP/2之服务器推送(Server Push)最佳实践

    商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处.   WeTest 导读 HTTP/1.X出色地满足互联网的普遍访问需求,但随着互联网的不断发展,其性能越来越成为瓶颈.IETF在2015 ...

  8. 洛谷 P3927 Factorial

    题目描述 SOL君很喜欢阶乘.而SOL菌很喜欢研究进制. 这一天,SOL君跟SOL菌炫技,随口算出了n的阶乘. SOL菌表示不服,立刻就要算这个数在k进制表示下末尾0的个数. 但是SOL菌太菜了于是请 ...

  9. 鼠标悬停,图片放大 CSS实现

    因为最近做的项目刚好用到了这个实现,分享出来   class=enlarge 为div标签的class div img 为标签 .enlarge div img:hover{ transform: s ...

  10. Struts2入门到放弃

    写在前面------------------------------------------------------------------------- 本文章主要从三个方面来学习Struts2框架 ...