这两天一直被事务的问题困扰,直到昨天才有突破性的进展,在此记录一下。
在做某业务逻辑的保存时,service的保存方法逻辑是分别对不同的表先后进行保存,而且service方法是加了事务控制的,但在测试过程中发现,即使保存出错,出错点保存的数据没有回滚,数据依旧是插入到相应的表中,也就是说,spring的事务控制没起作用!第一反应是,是不是我的配置有问题,
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref = "dataSource" />
</bean>
</pre><pre name="code" class="html"><tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Execption" />
</tx:attributes>
<aop:config>
<aop:pointcut id="allServiceMethod" expression="execution(* com.ll..service.impl.*.*(..))" />
<aop:advisor advice-ref="txAdvice" point-ref="allServiceMethod" />
</aop:config>
经过与老工程比较,发现这几个配置配的都没什么问题,那问题究竟出现在哪呢?挠头...
接着怀疑会不会是切点表达式写的不对,但经过个人的小例子验证,切点表达式写的也没问题,那会是什么问题呢?
接着思考,得看看Service的实现类是不是代理类,结果显示,的确没代理类。为什么没创建代理类呢?突然想起来,前段时候在利用老工程择出框架时,发现老工程里把spring配置文件spring-*.xml都放在类文件的根目录下,而因为如此,在web.xml里配置dispatcherServlet时,需要显式的初始化参数并指明spring配置文件地址;而Spring容器默认去WEB-INF下搜索spring配置文件(个人猜测,尚未验证),所以认为spring既然默认去WEB-INF下找,那应该是将Spring配置文件放在WEB-INF下更合理,故而挪过去了,
而类包的部署配置(包括Controller、service、dao)信息都放在了spring-servlet里,事务的配置信息都在spring-application里,会不会是因为配置信息在不同的文件导致的呢?为了验证,把Service的配置信息挪到spring-application里,经验证,果然,事务起效了,愁了几天的问题终于有突破性进展了。但依旧存在困惑:
1. dispatcherServlet是不是默认去WEB-INF下找Spring配置文件?
2.如果第一个问题的答案是“是”的话,那当Spring配置都在WEB-INF下时,事务起作用到底是因为只能放在相同的配置文件,还是因为在加载dispatcherServlet时重新加载了实例化了Service类导致的?(配置dispatcherServlet的servlet-name是spring,而Service的部署配置信息刚好在spring-servlet.xml里)
(6月16日更新)经过测试,得出最新结论:spring容器分父子容器,先启动父容器,即通过Web容器上下文参数contextConfigLocation指定的配置文件所加载的容器;然后再启动子容器,即DispatcherServlet,如果配置的时候没显式配置contextConfigLocation参数,则会按照Spring默认的规则<servlet-name>-servlet.xml寻找配置文件;否则将根据显式配置的配置文件加载子容器。
所以上面问题出现的原因就不言而明了:在加载父容器的时候,事务是生效了的,所有的Service类都生成了对应的代理类;但在加载子容器的时候,由于默认根据<servlet-name>-servlet.xml寻找配置文件,该该配置文件中没有事务控制,所以也生成了一份Service类,且没有创建事务的代理类,故而在Controller层调用Service时,实际调用的却是子容器创建的没有事务的对象。
而经过测试,在父容器中的实现类的确是代理类,而子容器中是原生的实现类对象。