Spring Boot 在Controller使用@Transactional注解导致@RequestMapping失效

笔者在基于 Spring Boot 开发服务框架时,想要在 Controller 层使用 @Transactional 注解引入 Spring 的事务管理,但是却遇上了很奇葩的问题。

例如以下代码,给 Controller 类的add方法加上@Transactional,这个 Controller 类使用 @RestController 注解,类名上也配置了 @RequestMapping,

    @RequestMapping(KiEntityActionName.ACTION_ADD + URL_EXTENSION)
    @Transactional(
            value = KiDataSourceConfig.TRADE_TX_MANAGER,
            timeout = 30,
            isolation = Isolation.READ_COMMITTED,
            propagation = Propagation.REQUIRED,
            readOnly = false,
            rollbackFor = Exception.class)
    @Override
    public Object add() {
        Map<String, Object> params = new HashMap<>();
        params.put("sellerId", 12);
        params.put("buyerId", 2);
        Object rtnData = this.bizService.execute(KiEntityActionEnum.ADD, params);
        return rtnData;
    }
然后启动应用程序,在控制台输出信息里,属于该类的所有 RequestMapping 全都不见了,正常应该出现如下信息,可以看到 "Mapped ****** " 等内容,表示使用@ReqeustMapping 配置的URL地址映射,但是该问题的奇葩之外在于,给任意方法加上 @Transactional 后,所有的映射就全部注入失败了,给整个类加上 @Transactional 也是同样问题,交换注解的顺序也没有用。

2017-11-21 17:32:00.431  INFO 3953 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5f058f00: startup date [Tue Nov 21 17:31:58 CST 2017]; root of context hierarchy
2017-11-21 17:32:00.487  INFO 3953 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/example/add.service]}" onto public java.lang.Object com.emulian.kiff.controller.KiExampleController.add()
2017-11-21 17:32:00.487  INFO 3953 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/example/one.service]}" onto public java.lang.Object com.emulian.kiff.controller.KiExampleController.one()
2017-11-21 17:32:00.488  INFO 3953 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/example/hello.service]}" onto public java.lang.String com.emulian.kiff.controller.KiExampleController.hello()
2017-11-21 17:32:00.488  INFO 3953 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/example/lst.service]}" onto public java.lang.Object com.emulian.kiff
一开始没有意识到是 @Transactional 注解的问题,找了很长时间错误原因。

后来笔者重新创建了一个 Controller 类进行问题复现,最终发现原来是上面的 Controller 类实现了笔者定义的另一个接口,类定义如下

@RestController
@RequestMapping(KiBizEntityName.ENTITY_EXAMPLE)
public class KiExampleController extends KiControllerAbstract implements KiControllerInterface {
}
也就是比一般定义的 Controller 多了一个 implements 的声明(继承抽像类不影响),这个时候虽然不影响 @RequestMapping 的注入,但是类内如果再增加 @Transactional 的话,整个类的所有注入就全部失效了,应该是 Spring 注入代理使用了 JDK 默认代理的原因。

解决方法

笔者又引入了 cglib 和 asm 两个依赖,并且在程序入口处增加了 @EnableAsync 注解,代码如下。引入 cglib 后 @RequestMapping 和 @Transactional 均可以正常使用,抛出异常后事务可以回滚。

@EnableTransactionManagement
@SpringBootApplication
/**
 * This @SpringBootApplication is a convenience annotation that adds all of the following:
 * @Configuration tags the class as a source of bean definitions for the application context.
 * @EnableAutoConfiguration tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
 * @EnableWebMvc Normally you would add @EnableWebMvc for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-webmvc on the classpath. This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.
 * @ComponentScan tells Spring to look for other components, configurations, and services in the hello package, allowing it to find the controllers.
 * */
@EnableAsync(proxyTargetClass = true)
public class KiApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(KiApplication.class, args);
    }
}
其实笔者增加 @EnableAsync 注解 也是因为看到如下的错误信息,配置注解的 proxyTargetClass=true 从而强制使用 cglib 来实现注入
***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'kiExampleBizService' could not be injected as a '*.*.kiff.service.biz.KiExampleBizService' because it is a JDK dynamic proxy that implements:
    *.*.kiff.interfaces.service.biz.KiBizServiceInterface


Action:

Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.


Process finished with exit code 1

最后附上 Maven 依赖定义

         <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>
        <dependency>
            <groupId>asm</groupId>
            <artifactId>asm</artifactId>
            <version>5.1</version>
        </dependency>

此外 Spring 使用 4.3.12.RELEASE,Maven项目直接继承(parent) spring-boot-starter-web, 版本 1.5.8.RELEASE,并增加 spring-tx,数据源依赖 druid,版本为 1.1.5,Java 8编译




  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值