You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring中Autowired配置类无法被Jackson序列化的原因排查

问题:Autowired注入的配置类序列化失败原因分析

问题背景

我正在学习云原生应用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 22:10:28