Spring中@Lazy修饰的prototype字段每次访问都返回新实例如何解决?
问题原因
这个现象是Spring原型作用域Bean配合懒注入的默认代理逻辑导致的:
- 你使用的
BaseTest是Spring托管的单例Bean(Spring测试类默认作用域为单例),单例Bean的属性注入只会在Bean初始化阶段执行一次。 - 你给注入字段添加了
@Lazy注解,同时@Scope注解未显式指定代理模式,Spring会自动为原型Bean生成CGLIB动态代理。这个代理的默认逻辑是每次调用代理对象的任意方法,都会重新生成一个新的原型Bean实例,所以你两次调用toggleTransactionHistoryWindow()方法时,操作的是两个不同的实例。 - 你对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
相关产品推荐
相关产品推荐

