单例Bean注入原型Bean时TestService的name属性为null问题咨询
问题根因
三个错误叠加导致name属性始终为null:
- 作用域代理的特性导致操作的不是同一个对象,且代理对象本身属性未初始化
你给TestService配置了ScopedProxyMode.TARGET_CLASS后,Spring注入到其他Bean里的永远是CGLIB生成的代理对象,不是真实的TestService实例。这个代理的逻辑是:每调用一次代理的公开方法,就会从容器拿一个全新的TestService原型实例,把这次方法调用转发给新实例。
你在test方法里先调testService.setName(a):这时代理会生成第一个TestService实例(name初始值是"hhh"),给它赋值a,方法执行完这个实例就被回收了,Spring不会缓存原型Bean。后面调testService.test()的时候,代理又生成第二个全新的TestService实例,这个实例从来没被赋值过。
至于为什么拿到的是null不是默认的"hhh":CGLIB生成代理对象时会绕过类的构造函数和成员初始化逻辑,代理对象自己的name字段根本没执行private String name = "hhh"的赋值,默认值就是null,只要你操作的是代理对象本身而非转发的真实实例,拿到的name就是null。 - @Lookup注解完全没生效
你虽然给getTestService()加了@Lookup,但两个写法错误导致它根本没用:- 业务逻辑里全程直接操作
testService字段,从来没调用过getTestService()方法,@Lookup每次调用返回新实例的逻辑根本触发不了。 - @Lookup标注的方法不需要自己写实现,Spring运行时会动态重写这个方法返回新的原型实例,你写的
return testService;逻辑本身是无效的。
- 业务逻辑里全程直接操作
- 字段缺少注入配置(大概率是贴代码时遗漏)
你贴的TestController代码里,testService字段没有加@Autowired/@Resource这类自动注入注解,正常情况下Spring不会给这个字段赋值,直接访问会抛空指针。就算你实际代码里加了注入注解,注入进来的也只是前面说的代理空壳,不是真实的TestService实例。
修复方式
两种原型Bean注入方案选一个就行,不要同时混用:
- 选@Lookup方案:删掉TestService上
@Scope注解里的proxyMode = ScopedProxyMode.TARGET_CLASS配置,只保留原型作用域声明;删掉TestController里的testService成员变量,在test方法里直接调用getTestService()拿到原型实例,所有set、test操作都在这个拿到的实例上执行。 - 选作用域代理方案:删掉@Lookup注解和对应的getTestService方法,注意不要在原型Bean里跨方法维护状态——因为每调用一次方法就会生成新实例,上一次方法设置的属性不会保留到下一次调用。
内容的提问来源于stack exchange,提问作者derrick.yang
相关产品推荐
相关产品推荐

