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

Spring原型Bean异常:调用Getter/Setter重复触发构造,实例判定不符合预期

Spring原型Bean异常:调用Getter/Setter重复触发构造,实例判定不符合预期

兄弟你遇到的这个问题其实是Spring中prototype scope结合<aop:scoped-proxy/>的特性导致的,我来给你拆解清楚:

问题根源1:为什么构造函数被反复调用?

当你给prototype类型的Bean加上<aop:scoped-proxy/>时,Spring并不会直接返回Notepad的实例,而是返回一个动态代理对象。

这个代理的行为是:每次你调用代理对象的任何方法(包括getA()、setA(),甚至toString()、hashCode()这些Object类的方法),它都会去ApplicationContext中重新获取一个全新的Notepad原型实例,然后调用这个新实例的对应方法。

所以你看到的构造函数反复触发,本质是每次方法调用都在创建新的原型Bean实例,这正是这个代理的设计逻辑——确保每次方法执行都用的是最新的原型实例。

问题根源2:为什么n1 == n2返回true,断言失败?

因为你通过context.getBean("notepad")拿到的n1和n2,都是同一个代理对象,而不是真正的Notepad实例。代理对象本身是单例的,所以用==比较自然会返回true,导致你的assertNotSame(n1, n2)断言失败。

但实际上,每次通过代理调用方法时,背后的实际Notepad实例都是不同的——你看测试日志里setA(1)用的是@4c2bb6e0,setA(2)用的是@2dc9b0f5,这两个就是完全不同的原型实例。

解决办法

根据你的需求场景,有两种处理方式:

1. 不需要代理场景:去掉<aop:scoped-proxy/>

如果你的场景只是单纯想每次getBean获取新的原型实例,不需要在单例Bean中注入原型Bean并保持每次调用都新实例的话,直接删掉<aop:scoped-proxy/>即可:

<bean id="notepad" class="Notepad" scope="prototype">
    <!-- 移除这行代理配置 -->
</bean>

修改后,每次context.getBean()都会返回全新的Notepad实例,构造函数只会调用两次,n1和n2会是不同的对象,assertNotSame(n1, n2)也会正常通过。

2. 需要代理场景:理解代理行为并调整验证逻辑

如果你确实需要使用scoped-proxy(比如在单例Bean中注入原型Bean,希望每次调用方法都拿到新实例),那就要接受代理对象是单例的事实:

  • n1 == n2为true是正常的,因为它们是同一个代理
  • 要验证实际的原型实例是否不同,可以通过日志里的实例ID(比如toString()输出的@后面的哈希值),或者在Notepad类中添加一个唯一标识属性(比如在构造函数中生成UUID)来区分。

比如修改Notepad类:

public class Notepad {
    private int a;
    private String uuid = UUID.randomUUID().toString();

    public Notepad() {
        System.out.println("  constructor called. this="+this+" hashCode="+this.hashCode()+" uuid="+uuid);
    }
    // 其他getA/setA方法不变
}

这样每次构造的实例UUID都不同,就能明确看到每次方法调用的实例都是新的。

备注:内容来源于stack exchange,提问作者Sydney Chen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 08:53:08