Spring中Autowired配置类无法被Jackson序列化的原因排查
问题背景
我正在学习云原生应用12要素中的Config主题,尝试将通过@Autowired注入的配置类转换为JSON字符串时遇到异常,但手动创建对象并赋值后序列化正常。
配置类代码
@Configuration @ConfigurationProperties(prefix = "accounts") @Getter @Setter public class AccountServiceConfig { private String msg; private String buildVersion; private Map<String, String> mailDetails; private List<String> activeBranches; }
正常运行的控制器代码
@RestController public class AccountsController { @Autowired public AccountServiceConfig accountServiceConfig; @GetMapping(value = "/account/properties") public String getPropertyDetails() throws JsonProcessingException { ObjectWriter ow = new ObjectMapper().writerWithDefaultPrettyPrinter(); Properties properties = new Properties(accountServiceConfig.getMsg(), accountServiceConfig.getBuildVersion(), accountServiceConfig.getMailDetails(), accountServiceConfig.getActiveBranches()); return ow.writeValueAsString(properties); } }
异常场景
当直接将@Autowired注入的accountServiceConfig传入writeValueAsString时,抛出以下异常:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.context.expression.StandardBeanExpressionResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.aht.account.config.AccountServiceConfig$$EnhancerBySpringCGLIB$$d99804b7["$$beanFactory"]->org.springframework.beans.factory.support.DefaultListableBeanFactory["beanExpressionResolver"]) ...(省略栈跟踪)
临时可行方案
手动创建AccountServiceConfig对象并从Autowired实例赋值后,序列化成功:
@GetMapping(value = "/account/properties") public String getPropertyDetails() throws JsonProcessingException { ObjectWriter ow = jacksonObjectMapper.writerWithDefaultPrettyPrinter(); AccountServiceConfig accountServiceConfig = new AccountServiceConfig(); accountServiceConfig.setMsg(this.accountServiceConfig.getMsg()); accountServiceConfig.setBuildVersion(this.accountServiceConfig.getBuildVersion()); accountServiceConfig.setMailDetails(this.accountServiceConfig.getMailDetails()); accountServiceConfig.setActiveBranches(this.accountServiceConfig.getActiveBranches()); return ow.writeValueAsString(accountServiceConfig); }
原因分析
你直接序列化的accountServiceConfig不是原始的AccountServiceConfig类实例,而是Spring通过CGLIB动态生成的代理对象——从异常信息里的AccountServiceConfig$$EnhancerBySpringCGLIB$$d99804b7就能明确这一点。
Spring会给标注了@Configuration的类生成代理,目的是实现配置类的特性(比如@Bean方法的懒加载、依赖注入的代理逻辑等)。这个代理对象除了包含你定义的属性外,还附带了Spring容器相关的内部属性(比如$$beanFactory),而这些内部类(像StandardBeanExpressionResolver、DefaultListableBeanFactory)没有被Jackson配置序列化规则,因此触发了序列化异常。
而你手动创建的对象是原始的AccountServiceConfig实例,没有Spring代理附加的内部属性,所以Jackson能正常序列化它的所有字段。
更优雅的解决方案
1. 移除配置类的@Configuration注解
如果这个类只是用来绑定配置(没有@Bean等配置类专属逻辑),可以直接去掉@Configuration,这样Spring不会为它生成代理,注入的就是原始实例,直接序列化即可。
2. 用@JsonIgnoreProperties忽略代理附加属性
在AccountServiceConfig类上添加注解,跳过Spring代理生成的内部属性:
@Configuration @ConfigurationProperties(prefix = "accounts") @Getter @Setter @JsonIgnoreProperties({"$$beanFactory", "$$beanClassLoader", "$$configurationClass"}) public class AccountServiceConfig { // 原有属性不变 }
Jackson序列化时会自动忽略这些代理相关字段,只处理你定义的属性。
3. 用BeanUtils快速复制属性
借助Spring的BeanUtils工具类,一键复制代理对象的属性到原始实例,避免手动赋值:
@GetMapping(value = "/account/properties") public String getPropertyDetails() throws JsonProcessingException { ObjectWriter ow = jacksonObjectMapper.writerWithDefaultPrettyPrinter(); AccountServiceConfig rawConfig = new AccountServiceConfig(); BeanUtils.copyProperties(accountServiceConfig, rawConfig); return ow.writeValueAsString(rawConfig); }
内容的提问来源于stack exchange,提问作者Arun Sudhakaran

