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

Spring中@Lazy修饰的prototype字段每次访问都返回新实例如何解决?

问题原因

这个现象是Spring原型作用域Bean配合懒注入的默认代理逻辑导致的:

  1. 你使用的BaseTest是Spring托管的单例Bean(Spring测试类默认作用域为单例),单例Bean的属性注入只会在Bean初始化阶段执行一次。
  2. 你给注入字段添加了@Lazy注解,同时@Scope注解未显式指定代理模式,Spring会自动为原型Bean生成CGLIB动态代理。这个代理的默认逻辑是每次调用代理对象的任意方法,都会重新生成一个新的原型Bean实例,所以你两次调用toggleTransactionHistoryWindow()方法时,操作的是两个不同的实例。
  3. 你对prototype作用域的理解是正确的:正常情况下仅在注入时生成新实例,当前异常行为是代理逻辑导致的,不是作用域本身的问题。
解决方案

根据你的业务需求选择对应方案即可:

方案1:每个测试类仅初始化一次PageObject实例

如果你希望整个测试类的生命周期内仅使用同一个DashboardPage实例,修改@PageObject注解的@Scope配置,关闭方法级代理:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Lazy
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.NO)
public @interface PageObject {
}

同时移除BaseTest中注入字段上的@Lazy注解即可。

方案2:每个测试方法初始化一次PageObject实例

如果你希望每个测试方法执行时使用新的DashboardPage实例,不要直接字段注入,改为主动从Spring上下文获取实例:
首先在BaseTest中注入Spring上下文:

@Autowired
private ApplicationContext applicationContext;

// 新增获取方法
protected <T> T getPageObject(Class<T> pageClazz) {
    return applicationContext.getBean(pageClazz);
}

测试代码中主动拉取实例复用:

@Test
void transactionHistorySmokeTest() {
    DashboardPage dashboardPage = getPageObject(DashboardPage.class);
    dashboardPage.toggleTransactionHistoryWindow();
    dashboardPage.toggleTransactionHistoryWindow();
}

方案3:保留每次方法调用新建实例的逻辑

如果你确实需要每次调用Bean方法都生成新实例,当前行为符合预期,只要把首次访问的实例缓存下来复用即可:

@Test
void transactionHistorySmokeTest() {
    // 首次访问代理生成实例,存到局部变量复用
    DashboardPage page = dashboardPage;
    page.toggleTransactionHistoryWindow();
    page.toggleTransactionHistoryWindow();
}

内容的提问来源于stack exchange,提问作者Rasire

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 00:09:03