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
相关产品推荐
相关产品推荐

