【发布时间】:2014-10-30 05:21:09
【问题描述】:
我们一直在使用 @Autowired 加上基于 Java 的 Spring 配置并取得了一些成功,但现在,我们正在失去控制。每个人都开始到处添加自动装配的依赖项,造成循环和奇怪的错误。
所以我们正在考虑使用构造函数注入和 Spring 配置的自动装配。
旧:
class Bean {
@Autowired Foo foo;
}
@Configuration
@Import( FooCfg.class )
class BeanCfg {
@Bean public Bean bean() { return new Bean(); }
}
新:
class Bean {
public Bean(Foo foo) {...}
}
@Configuration
class BeanCfg {
@Autowired FooCfg fooCfg;
@Bean public Bean bean() { return new Bean(fooCfg.foo()); }
}
这很好用(它驱使人们拆分 bean,而不是使用 10 多个构造函数参数创建怪物)。
但是当Bean 有一个用@Transactional 注释的方法时它会失败,因为CGLIB 然后尝试创建一个代理失败,因为它找不到无参数的构造函数。
解决办法是什么?
【问题讨论】:
-
我只会重构
@Transactional类以提供具有业务逻辑的接口 - 并注入这些业务接口,而不需要使用 cglib。 -
使用与类相同的方法创建接口,例如
void saveSomething(Something a),并将该接口注入客户端 bean,而不是注入类实例。原始类(注册为 Spring bean)将实现该接口,但 客户端 将通过接口引用它。附言将@Transactional留在方法的实现上,不要将其移至接口。 -
当使用接口时,它会导致使用 JDK 动态代理,它可以由任意 bean 创建。您可能还想尝试升级 spring 版本(到 4.0 或 4.1),因为我记得该区域的一些修复应该允许 cglib 在这些情况下工作。但是我仍然更喜欢界面,但这只是我。还有一个关于构造函数注入的注释,它也可以与
@Autowired一起使用,只需用@Autowired而不是字段来注释你的构造函数。同样对于 cglib 来说,拥有一个受保护的构造函数就足够了,它只需要它来进行子类化。 -
这两种说法都来自Java语言的限制。 CGLIB 使用继承来生成代理类,但是在创建子类实例时不能绕过调用“super()”构造方法。一些语言缓解了这个问题,例如在 Google Dart 中,类会自动定义接口。另一个问题是注解不是inherited from interfaces,所以接口上的
@Transactional并不总是有效。所以这些问题只是 Java/JVM 的陷阱。 -
基本上,是的。但如果“任何东西”实际上是一个 java 接口,那么 Spring 框架根本不会使用 cglib,而是使用标准的JDK API。还要记住,cglib 代理类继承了所有字段,但它们从不使用,因为代理方法调用会将工作委托给代理后面的原始 bean(并且您必须注意所有公共方法都不是最终的),所以java 接口的方法更简洁一些。
标签: spring autowired transactional proxy-classes cglib