同时使用springboot定时任务+websocket启动报错

这里是引用

问题描述

项目中需要用到websocket,但是websocket协议本身有没有心跳机制,如果没有心跳检测,服务端就可能会产生大量的垃圾链接,所以我们需要设置心跳,定时清除无关的连接,故使用的定时任务来做wesocket的心跳检测,不过在同时使用websocket和scheduler时,启动服务会报 “Bean named ‘defaultSockJsTaskScheduler’ is expected to be of type ‘org.springframework.scheduling.TaskScheduler’ but was actually of type ‘org.springframework.beans.factory.support.NullBean’”错误。

错误

Bean named ‘defaultSockJsTaskScheduler’ is expected to be of type ‘org.springframework.scheduling.TaskScheduler’ but was actually of type ‘org.springframework.beans.factory.support.NullBean’

原因排查

报错中创建TaskScheduler类型对象名称为 defaultSockJsTaskScheduler的类失败因为它是个null,想着SpringBoot在使用定时任务默认就回创建一个单例的调度器线程池。不应该没有TaskScheduler类型对象

解决

随后就去百度,搜索到的结果基本都是一样的,原因是swebsocket 默认实现了一个ThreadPoolTaskScheduler 导致与spring创建TaskScheduler 冲突,需要手动实现一个TaskScheduler,果然手动创建一个TaskScheduler确实解决了问题。
在这里插入图片描述

报错真正原因分析

报错导致的问题就是没有TaskScheduler类导致的报错,原因确实是使用websocket后,TaskScheduler创建冲突的问题。

a.根据报错中defaultSockJsTaskScheduler找到websocket源码如下,在初始化websocket时WebSocketConfigurationSupport.class 类中会执行defaultSockJsTaskScheduler()方法,返回一TaskScheduler对象,此时this.TaskScheduler 为null , 但是在判断中 initHandlerRegistry().requiresTaskScheduler() 检验调度器是否必须时,由于使用@Bean及@Nullable允许创建一个Bean对象为Null(这就是为报错埋下的一个大坑)
在这里插入图片描述

b. 在此时初始化spring容器中,就存在类名为defaultSockJsTaskScheduler,类型TaskScheduler的对象,但是此对象为null

c.由于使用@EnableScheduling注解开启spring任务调度器,之后系统在初始化Spring自带的调度器时,ScheduledAnnotationBeanPostProcessor.class 在初始化是会在Spring容器中加载TaskScheduler对象时会根据类型(TaskScheduler)去spring容器中获取对象,这时候就获取到了在websocket加载后创建为null的defaultSockJsTaskScheduler对象。(这才是导致报错的根本原因)

在这里插入图片描述

解决方案依旧是自己重新创建一个TaskScheduler Bean对象到spring容器,来填上websocket配置初始化加载创建一个TaskScheduler为 null的对象 产生的坑