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

Spring Boot @RequestScope Bean构造销毁日志无输出及计数器未重置问题

问题根源与解决方案

核心原因:单例Controller注入请求域Bean时,拿到的是代理对象

你的Controller是Spring默认的单例作用域,当你直接在类上@Autowired标注@RequestScope的Person Bean时,Spring并不会直接注入请求级的实例,而是注入一个代理对象。这个代理对象的作用是在每次请求到来时,动态获取当前请求对应的Person实例;请求结束后,销毁该实例。

逐个解释你的现象:

  • 直接操作person1.counter++不重置,无构造/销毁日志
    代理对象本身是单例的,和Controller的生命周期绑定。当你直接修改代理对象的公共属性counter时,修改的是代理对象自身的属性,而不是当前请求对应的实际Person实例的属性。这种情况下,代理根本没触发创建真实的Person实例,自然看不到构造和销毁日志,计数器也不会随请求重置。

  • 调用increaseCounter()方法时一切正常
    当你调用代理对象的方法时,代理会自动将方法调用转发给当前请求对应的真实Person实例。此时Spring才会创建请求级的Person实例(打印构造日志),方法操作的是真实实例的counter;请求结束后,实例被销毁(打印销毁日志),下次请求会创建新的实例,计数器自然重置。

解决方案

方案1:封装属性操作,通过方法访问(推荐)

把Person的属性改为私有,提供对应的方法来操作,避免直接访问属性:

@Component
@RequestScope
public class Person {
  // 改为私有属性
  private String name;
  private int counter;
  
  public Person() {
    System.out.println("Person Created");
  }
  
  @PreDestroy
  public void destroy() {
    System.out.println("Person Destroyed");
  }
  
  // 封装计数器递增方法
  public int increaseCounter() {
    return ++counter;
  }
  
  // getter/setter
  public String getName() {
    return name;
  }
  
  public void setName(String name) {
    this.name = name;
  }
  
  public int getCounter() {
    return counter;
  }
}

Controller中调用方法而非直接操作属性:

@RestController
public class Controller {
  @Autowired Person person1;
  @Autowired Person person2;
  
  @RequestMapping("setPersonName")
  void setPerson() {
    person1.setName("John");
    person2.setName("Bill");
    person1.increaseCounter(); // 调用方法操作计数器
  }
  
  @RequestMapping("getPersonName")
  String getPerson() {
    return person1.getCounter() + ":" + person1.getName() + " - " + person2.getName();
  }
}

方案2:在Controller方法参数中注入RequestScope Bean

每次请求进入方法时,Spring会直接注入当前请求对应的Person实例,绕开代理的属性问题:

@RestController
public class Controller {
  
  @RequestMapping("setPersonName")
  void setPerson(@Autowired Person person1, @Autowired Person person2) {
    person1.name = "John";
    person2.name = "Bill";
    person1.counter++;
  }
  
  @RequestMapping("getPersonName")
  String getPerson(@Autowired Person person1, @Autowired Person person2) {
    return person1.counter + ":" + person1.name + " - " + person2.name;
  }
}

方案3:使用ObjectFactory获取实例

通过ObjectFactory延迟获取当前请求的实例,确保每次拿到的都是请求级对象:

@RestController
public class Controller {
  @Autowired ObjectFactory<Person> person1Factory;
  @Autowired ObjectFactory<Person> person2Factory;
  
  @RequestMapping("setPersonName")
  void setPerson() {
    Person person1 = person1Factory.getObject();
    Person person2 = person2Factory.getObject();
    person1.name = "John";
    person2.name = "Bill";
    person1.counter++;
  }
  
  @RequestMapping("getPersonName")
  String getPerson() {
    Person person1 = person1Factory.getObject();
    Person person2 = person2Factory.getObject();
    return person1.counter + ":" + person1.name + " - " + person2.name;
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 02:23:16