Spring MVC @Transactional注解方式事务失效的解决办法

前文提到,最新换了框架,新项目用SpringMVC + Spring JdbcTemplate。搭框架时,发现了一个事务无法正常回滚的问题,记录如下:

首先展示问题:

SpringapplicationContext.xml配置:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiName"><value>java:comp/env/jdbc/will</value></property></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource" /></bean><bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!– 事务控制 –><tx:annotation-driven transaction-manager="txManager" />

Spring mvc.dispatcher.xml配置:

<!– 自动扫描的包名 –><context:component-scan base-package="com.will" ></context:component-scan><!– 默认的注解映射的支持 –><mvc:annotation-driven /><!– 对静态资源文件的访问 –><mvc:default-servlet-handler/><!– 拦截器<mvc:interceptors><bean class="com.will.mvc.MyInteceptor" /></mvc:interceptors>–><!– 视图解释类 –><bean id="viewResolver"class="org.springframework.web.servlet.view.UrlBasedViewResolver"><property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /><property name="prefix" value="/WEB-INF/pages/" /><property name="suffix" value=".jsp" /></bean> 然后在Service层模拟了一个事务回滚的method case: @Transactionalpublic boolean save(Person person){for(int id: new int[]{2,3}){personDao.del(id);int j = 1/0;}return false;}本以为大功告成,在运行save方法时,由于1/0 抛出java.lang.ArithmeticException: / by zeroRuntimeException,导致事务回归。However,no way! So crazy~

查了下,发现Spring MVC对于事务配置比较讲究,需要额外的配置。解决办法如下:

需要在applicationContext.xml增加:

<context:component-scan base-package="com.will"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>在Spring mvc.dispatcher.xml增加:<context:component-scan base-package="com.will" ><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" /> </context:component-scan>

由于web.xml中配置:

<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath*:/mvc_dispatcher_servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping>

Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvc_dispatcher_servlet.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,,而父容器进行初始化的Service是保证事务的增强处理能力的。如果不在子容器中将Service exclude掉,此时得到的将是原样的无事务处理能力的Service。

( update 2014.05.27 今天看见一种说法:key word =双亲上下文。不使用ContextLoaderListener监听器来加载spring的配置,改用DispatcherServlet来加载spring的配置,不要双亲上下文,只使用一个DispatcherServlet就不会出现上述问题。笔者这里未测过这个办法,因为我自己的业务需要一个extendsContextLoaderListener的selfListener,有兴趣的朋友可以自己测试下这个说法,并欢迎把测试的结果与我交流 :))

经过以上分析,故可以优化上述配置:

在applicationContext.xml增加:

<context:component-scan base-package="com.will"> </context:component-scan>在Spring mvc.dispatcher.xml增加:<context:component-scan base-package="com.will" ><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" /> </context:component-scan>

接受失败,是我们不常听到或看到的一个命题,我们大都接受的是正面的教育,

Spring MVC @Transactional注解方式事务失效的解决办法

相关文章:

你感兴趣的文章:

标签云: