You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Controller调用saveOrUpdate时Transaction Interceptor未触发问题求助

问题分析与解决方案

看起来你遇到了一个典型的Spring事务在Web环境中不生效,但JUnit测试正常的问题。我帮你梳理一下可能的原因和对应的解决办法:

最可能的原因:Spring上下文加载冲突

JUnit测试时直接加载了根上下文(application-main-context.xml),里面包含了事务配置和Service层的Bean定义,所以事务代理正常生效。但在Web环境中,如果你没有正确分离根上下文和DispatcherServlet上下文的职责,就会导致Service层的Bean被重复创建,且没有被事务代理。

解决步骤:

  1. 分离上下文扫描范围

    • 根上下文(由ContextLoaderListener加载的application-main-context.xml)负责扫描非Web层组件(Service、Dao、Repository等),同时包含事务配置:
      <context:component-scan base-package="com.example">
          <!-- 排除Controller,交给DispatcherServlet上下文处理 -->
          <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
      </context:component-scan>
      <!-- 保留你的事务配置(tx:annotation-driven、transactionManager、tx:advice、aop:config) -->
      
    • DispatcherServlet的配置文件(比如spring-mvc.xml)只负责扫描Controller层,避免重复创建Service Bean:
      <context:component-scan base-package="com.example">
          <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
          <!-- 明确排除Service和Repository,防止重复实例化 -->
          <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
          <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
      </context:component-scan>
      <!-- 这里只放MVC相关配置:视图解析器、RequestMappingHandlerMapping等 -->
      
  2. 确认Web.xml中配置了ContextLoaderListener
    确保根上下文被正确加载,否则事务配置和Service Bean都不会初始化:

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application-main-context.xml</param-value>
    </context-param>
    
    <!-- 你的DispatcherServlet配置 -->
    <servlet>
        <servlet-name>spring-mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

其他排查点

  1. 验证Service是否被事务代理
    在Controller中添加一行代码打印Service的类名:

    System.out.println("Service类名:" + entityService.getClass().getName());
    

    如果输出的是原始类名(比如com.example.service.UserHistoryServiceImpl),说明没有被代理;正常应该是代理类名(比如com.example.service.UserHistoryServiceImpl$$EnhancerBySpringCGLIB$$xxxx或者JDK代理的类名)。

  2. 检查AOP切入点表达式是否匹配
    你的AOP切入点是execution(* com.example.service..*Service.*(..)),确认你的UserHistoryService是否在com.example.service包或子包下,且类名以Service结尾(比如UserHistoryServiceImpl实现UserHistoryService接口,这个是符合的,但要确保包路径正确)。

  3. 避免事务配置重复
    你同时使用了<tx:annotation-driven>和基于AOP的<tx:advice>,虽然两者可以共存,但建议统一配置方式:要么全用注解式事务(在Service类/方法上加@Transactional),要么全用AOP的XML配置,避免潜在冲突。

额外提示

你的OpenSessionInViewFilter配置没问题,但要确保它在web.xml的Filter顺序中是第一个,这样能保证Session在请求开始时就被创建,避免懒加载问题。


内容的提问来源于stack exchange,提问作者Horn Daneth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:03:23