您的位置:首页 > 移动开发

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

2017-11-21 17:35 1706 查看
笔者在基于 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编译
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息