为何Spring单例Bean在字段与方法参数@Autowire时实例不同?
问题描述
调用GET /test接口(对应方法BeanScopeTestController#checkTheSameInstance)时,输出结果为method equals: false; instance equals: false,这是为何?
Spring中@Bean的默认作用域是Singleton,原本期望类字段singletonBean1和方法参数singletonBean2从Spring容器中获取同一个实例,但实际注入的是SingletonBean的不同实例。是Spring MVC中方法参数上的@Autowire工作机制不同导致作用域被覆盖,还是另有原因?
预期是字段和方法参数注入的是同一个SingletonBean实例。
相关代码
BeanScopeTestController.java
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") @Slf4j class BeanScopeTestController { private final SingletonBean singletonBean1; @Autowired BeanScopeTestController(SingletonBean singletonBean) { this.singletonBean1 = singletonBean; } @GetMapping String checkTheSameInstance(@Autowired SingletonBean singletonBean2) { log.info(singletonBean1.toString()); log.info(singletonBean2.toString()); boolean methodEquals = singletonBean1.equals(singletonBean2); boolean instanceEquals = singletonBean1 == singletonBean2; return String.format("method equals: %s; instance equals: %s", methodEquals, instanceEquals); } }
SingletonBean.java
import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Slf4j @Component @ToString @EqualsAndHashCode class SingletonBean { private static int idProvider = 0; private final int id; public SingletonBean() { this.id = idProvider++; } }
SpringDemoApplication.java
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringDemoApplication.class, args); } }
问题原因
核心问题在于Bean类的访问修饰符:
SingletonBean和BeanScopeTestController都使用了默认的包访问权限(未添加public修饰符)。- Spring的组件扫描机制默认只会识别并托管
public修饰的类作为Bean。对于非public的类,即使添加了@Component或@RestController注解,Spring容器无法将其纳入单例管理逻辑,每次注入时都会通过反射创建一个新的实例。 - 这和Spring MVC中方法参数的
@Autowired机制无关,本质是目标Bean没有被Spring容器正确管理为单例。
解决方法
给SingletonBean和BeanScopeTestController添加public修饰符,让Spring组件扫描能正确识别并将它们作为单例Bean存入容器:
修改后的SingletonBean.java:
import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Slf4j @Component @ToString @EqualsAndHashCode public class SingletonBean { // 添加public修饰符 private static int idProvider = 0; private final int id; public SingletonBean() { this.id = idProvider++; } }
修改后的BeanScopeTestController.java:
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") @Slf4j public class BeanScopeTestController { // 添加public修饰符 private final SingletonBean singletonBean1; @Autowired BeanScopeTestController(SingletonBean singletonBean) { this.singletonBean1 = singletonBean; } @GetMapping String checkTheSameInstance(@Autowired SingletonBean singletonBean2) { log.info(singletonBean1.toString()); log.info(singletonBean2.toString()); boolean methodEquals = singletonBean1.equals(singletonBean2); boolean instanceEquals = singletonBean1 == singletonBean2; return String.format("method equals: %s; instance equals: %s", methodEquals, instanceEquals); } }
修改后再次调用接口,会得到预期结果:method equals: true; instance equals: true。
内容的提问来源于stack exchange,提问作者Jasiu
相关产品推荐
相关产品推荐

