寻找有状态单例Bean

6
今天,我们在代码中发现了这个模式:
class Foo {
    private List<String> errors;

    public void addError(String error) { ... }
    public List<String> getErrors();
}

尽管代码似乎可以工作,但这是一个单例Spring Bean,并且被注入到多个独立的地方,Bean的使用者假设他们每个人都有自己的错误列表。因此,这会引入微妙的错误。

显而易见的解决方案是教育开发人员避免这种错误,但我想知道是否有静态或运行时代码分析工具可以发现这种错误。

例如,Bean后处理器可以在返回Bean之前分析Bean并查找未标记为@Autowired的私有字段。


我们可以使用@postConstruct来重置私有字段吗? - sreeprasad
@SREEPRASADGOVINDANKUTTY:你可以尝试,但是它不会起作用,因为在创建Foo时只会调用一次@PostConstruct。你不能在bean A中使用@PostConstruct来重置字段,因为这也会清除bean B的列表。 - Aaron Digulla
虽然不是很规范的方式,但你可以尝试使用Spring AOP,在afterAdvice中添加方法,然后在该方法中检查这些字段。 - Sachin Thapa
如果我们使用原型(prototype)而不是单例(singleton)作用域,那么每次需要对象时都会创建new Foo(),而每个开发人员都会拥有自己的错误列表? - sreeprasad
@SREEPRASADGOVINDANKUTTY:将Foo转换为原型是一种解决方法,但它并不能解决错误。我希望有一个单元测试能够告诉开发人员当他们犯这种错误时。 - Aaron Digulla
2个回答

1
在我们(包括自己和他人)的努力下,我们想出了以下方法:
  1. 安装一个BeanPostProcessor,确保所有单例bean(即bean定义中范围为Singleton的bean)具有实际bean类型上的自定义注释@Stateless

    我们选择使用自定义注释而不是重用@Singleton,因为我们在其他地方也需要此功能。

    如果缺少注释,则工厂将抛出错误。

  2. 在单元测试中,我们使用ClassPathScanningCandidateComponentProvider和我们的自定义注释来定位类路径上的所有类。然后,我们可以进行复杂且昂贵的测试,以确保bean在初始配置之后没有更改状态(即在自动装配发生之后)。

如果我们将自动装配字段移入构造函数中,第二步可能会变得更加容易,但我们不喜欢具有许多参数的方法。如果Java或IDE可以从bean代码生成构建器,那就太好了。由于这并非如此,我们坚持使用自动装配字段和/或设置器。


0
你可以创建一个 JUnit 测试来加载你的应用程序配置。这可以结合这里的 ListableBeanFactory: 我能通过扫描 Spring 配置文件中的 bean 动态创建列表吗? 与这里的 'isSingleton' 检查: 如何强制执行 Spring bean 的原型范围 即列出应用程序上下文中的所有 bean,然后检查哪些是单例的。
这将让你找到所有的单例 bean……虽然它不能真正防止你的错误情况,其中有人将其中一个单例视为非单例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接