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
相关产品推荐
相关产品推荐

