使用SpEL在Controller注入Service层属性始终为null的问题排查
问题原因及解决方案
核心原因
你的DummyServiceLayer类因为标注了@Transactional和@Async注解,Spring会为其生成CGLIB动态代理对象(由于类未实现接口,默认使用CGLIB代理)。
Spring的代理机制中,代理类仅负责方法的增强逻辑(事务、异步),原始对象的字段值不会复制到代理对象的同名字段中:
dbProperty字段的注入是直接赋值给原始的DummyServiceLayer实例的- 你通过
@Value("#{@dummyServiceLayer.dbProperty}")获取的是代理对象的dbProperty字段,而这个字段并未被Spring赋值,因此返回null - 调试时看到的"正确值",实际是代理对象内部持有的原始目标对象的字段值,而非代理对象自身的字段值
解决方案
方案1:使用Getter方法访问属性(推荐)
修改DummyServiceLayer,将dbProperty改为私有字段并添加getter方法,通过SpEL调用getter而非直接访问字段:
@Service public class DummyServiceLayer { @Autowired JdbcTemplate jdbcTemplate; @Autowired NamedParameterJdbcTemplate namedJdbcTemplateMaster; @Value("#{dummyLayer2.getDbProperty('dummyString')}") private String dbProperty; // 改为私有字段 @Value("some value") private String dbProperty1; // 改为私有字段 // 添加属性getter public String getDbProperty() { return dbProperty; } @Transactional(propagation = Propagation.REQUIRES_NEW) @Async("asyncTaskWorkerPool") public void doingDBstuff() throws Exception { System.out.println("object hash : "+this); System.out.println("DB property fetched using spring expression library: "+dbProperty); } }
然后修改Controller中的SpEL表达式,调用getter方法(SpEL会自动匹配getDbProperty方法):
@Value("#{@dummyServiceLayer.dbProperty}") String value4;
方案2:直接通过注入的Service实例访问(更简洁)
既然你已经通过@Autowired注入了dummyLayer,完全可以跳过SpEL注入,直接在方法中通过getter访问属性:
// 移除Controller中的@Value("#{@dummyServiceLayer.dbProperty}") String value4; @GetMapping("/dummyGet") public ResponseEntity<?> getMethodDummy() throws Exception { System.out.println("value 3 is : " + value3); System.out.println("value 4 is : " + dummyLayer.getDbProperty()); // 调用getter获取属性 dummyLayer.doingDBstuff(); return new ResponseEntity("Api Completed", HttpStatus.OK); }
额外建议
- 遵循封装原则,永远使用私有字段+getter/setter,避免直接暴露public字段,这不仅能规避代理相关问题,还能提升代码可维护性
- 不要尝试直接获取原始对象绕过代理,这会破坏Spring的事务、异步等增强逻辑
内容的提问来源于stack exchange,提问作者Manish Kumar
相关产品推荐
相关产品推荐

