要解釋清我遇到的情況首先要了解以下幾個知識點:
(Ps:造成@Transactional不生效的原因有很多種,可以參考:https://blog.csdn.net/qq_20597727/article/details/84900994)
1、Spring MVC項目中會有兩個容器初始化,配置了Spring註解(例:@Transactional、@Controller、@Service等註解)的類(class)
- DispatcherServlet一般用來加載MVC相關的類(本文這個過程簡稱:加載springmvc配置)
// springmvc配置文件在web.xml中如何配置的
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
// springmvc.xml的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.isdev.ssm.**" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven/>
</beans>
- ContextLoaderListener一般用來加載整個Spring相關的類(本文這個過程簡稱:加載spring配置)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
// spring.xml的簡單配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.isdev.ssm.**">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven/>
<!--
4. 事務管理 : DataSourceTransactionManager dataSource:引用上面定義的數據源
-->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5. 使用聲明式事務
transaction-manager:引用上面定義的事務管理器
-->
<tx:annotation-driven transaction-manager="txManager" />
</beans>
- 爲什麼要分兩個容器進行初始化可以參考文章:https://blog.csdn.net/py_xin/article/details/52052627
2、要使spring註解配置生效需要在springmvc.xml配置中或者spring.xml加入<context:component-scan>標籤,如何配置<context:component-scan>是本文的關鍵
3、spring的配置文件與springmvc的配置文件分開加載,在spring容器初始化的時候,會先加載spring配置(即:web.xml中中的配置),之後再加載springmvc的配置(即:web.xml中中的中的配置)。加載springmvc的配置的時候,如果掃面到spring配置掃描加載過的bean也會重新加載,並覆蓋spring配置掃描加載的bean,但springmvc加載的bean都是沒有aop配置的(@Transactional本事也是一種aop),這樣就會導致配置了@Transactional註解的bean事務失效。那麼要解決@Transaction註解的方法就有了,只要保證配置了@Transactional的bean不被springmvc配置中的<context:component-scan>掃描到即可
解決方案
springmvc.xml
/**
* 1、配置白名單,只掃描controller層bean,其他交給spring.xml初始化
* 2、component-scan:黑白名單默認過濾
* 只配置黑名單(exclude-filter):黑名單內的bean不被初始化,其他的均被初始化
* 只配置白名單(include-filter):默認:::白名單內的bean初始化,既不在白名單也不在黑明單的bean也會被初始化
* 3、要想把component-scan默認過濾關閉,只初始化白名單中的bean,需配置use-default-filters="false"
*/
<context:component-scan base-package="cn.isdev.ssm.**" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
spring.xml
// 掃描時除controller意外的所有bean(這樣就包括配置了@Transaction的service了)
<context:component-scan base-package="cn.isdev.ssm.**">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>