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

SpringBoot+Redisson调度任务Bean序列化栈溢出问题求助

问题分析

从堆栈日志可以明确:JobService依赖的RedisTemplate存在循环引用(RedisTemplate包含clusterOps,clusterOps又持有RedisTemplate实例),Kryo序列化时会递归遍历这些引用,最终触发栈溢出。而你直接将Spring容器中已注入依赖的MyScheduledTask Bean传给Redisson调度器,导致序列化时尝试处理整个依赖链。

解决方案

方案1:任务元数据传递+Spring上下文获取Bean(推荐)

避免序列化Spring管理的Bean,改为传递任务标识,在任务执行时从Spring上下文动态获取依赖Bean。

1. 修改任务类

@Component
public abstract class MyScheduledTask implements Runnable {
    private static ApplicationContext applicationContext;
    String taskName;
    private RedisJobDetail jobDetail;

    // 初始化Spring上下文引用
    @Autowired
    public void setApplicationContext(ApplicationContext context) {
        applicationContext = context;
    }

    public void setJobDetail(RedisJobDetail jobDetail) {
        this.jobDetail = jobDetail;
    }

    public abstract void initJob();

    @Override
    public void run() {
        // 从上下文获取依赖Bean
        JobService jobService = applicationContext.getBean(JobService.class);
        jobService.printttt();
        // 执行业务逻辑
        doLogic(jobDetail.getJobDataMap());
    }

    protected abstract void doLogic(Map<String, String> param) throws Exception;

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
}

2. 调整调度任务提交代码

RedisJobDetail redisJobDetail = new RedisJobDetail();
redisJobDetail.setJobClass(MyScheduledTaskChild.class);
String name = "MyScheduledTask2";
Map<String, String> cfg = new HashMap<>();
cfg.put("jobName", name);
redisJobDetail.setJobDataMap(cfg);

// 直接提交JobDetail,而非Spring Bean实例
executorService.scheduleAsync(redisJobDetail, CronSchedule.of("0 47/1 * ? * * *"));

方案2:开启Kryo引用跟踪

如果必须序列化带依赖的Bean,可修改Redisson的Codec配置,开启Kryo的引用支持,让它自动处理循环引用:

Config config = new Config();
// 配置Kryo并启用引用跟踪
KryoCodec codec = new KryoCodec();
codec.getKryo().setReferences(true);
config.setCodec(codec);

// 集群配置保持不变
config.useClusterServers().addNodeAddress("redis://127.0.0.1:1001")
        .setPassword("redisHostPassword");

RedissonClient redisson = Redisson.create(config);

方案3:自定义序列化器排除循环字段

通过自定义Kryo序列化器,手动排除导致循环引用的字段:

// 自定义RedisTemplate序列化器
public class RedisTemplateSerializer extends FieldSerializer<RedisTemplate> {
    public RedisTemplateSerializer(Kryo kryo) {
        super(kryo, RedisTemplate.class);
        // 排除clusterOps字段,切断循环引用链
        removeField("clusterOps");
    }
}

// 配置Redisson时注册自定义序列化器
Config config = new Config();
KryoCodec codec = new KryoCodec();
Kryo kryo = codec.getKryo();
kryo.setReferences(true);
kryo.register(RedisTemplate.class, new RedisTemplateSerializer(kryo));
config.setCodec(codec);
方案选型建议

方案1是最优解:Spring管理的Bean包含大量容器相关引用,序列化这些Bean不仅容易出现循环引用问题,还会导致序列化数据体积过大,影响性能和存储。方案1通过传递任务元数据,在执行时动态获取Bean,既符合Spring依赖注入的设计思想,也从根源避免了序列化问题。

内容的提问来源于stack exchange,提问作者hani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:15:18