SpringBoot——定时任务+WebSocket(问题)

  开发环境:win7 + idea2018 + jdk 1.8 + springboot 2.x

  记一次出现问题,我在项目中先集成了websocket环境,并且测试通过,之后想要模拟实时推送的效果,虽然可以直接使用线程类进行模拟,但是想到最近看到了定时任务,就像试一试,没有想到,这不试不知道啊,这里有个坑,主要原因还是由于个人的基础太浅薄,英文水平太辣鸡。

    WebSocket环境搭建可以参考我之前的记录(https://www.cnblogs.com/threadj/p/10552904.html),在这里只记录本次出现问题

首先是搭建定时任务环境:

1、创建定时任务,如下使用@Scheduled注解并指定其时间周期,该方法为一个定时任务,使用@Component将定时任务类加入Spring管理

 

未验证:都说定时任务只能放在一个单独的类里面,就比如说不可以在@Controller中直接用@Scheduled注解标注一个定时任务,这个暂时我还没有进行验证

package com.zyzj.site_civilization.task;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zyzj.site_civilization.net.websocket.WebSocketHandler;
import com.zyzj.site_civilization.net.websocket.WebSocketPool;
import com.zyzj.site_civilization.service.EquipmentService;
import com.zyzj.site_civilization.util.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.websocket.Session;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class DataSupportTask implements InitializingBean {

    @Autowired
    private EquipmentService equipmentService;

    private Map<String, Session> map;
    private ObjectMapper mapper = new ObjectMapper();
    private List<Equipment> equipmentList;


    public EquipmentDataHistory init(){
        //初始化数据
        类 data = new 类();//嘿嘿
        return data;
    }

    //定时任务执行体,5秒执行一次,fixedRate具体意义不说了,因为我忘了
    @Scheduled(fixedRate = 5000)
    public void dataSupport(){
        map = WebSocketPool.getSession();
        if (map.size() > 0){
            try {
                WebSocketHandler.sendMessageAll(mapper.writeValueAsString(init()));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }else {
            log.info("当前无在线用户");
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //这个方法会在IOC对当前类进行实例化时,当所有的依赖注入完成后调用此方法
        //我不知道怎么表达对不对
        //我这里因为我在init方法中需要使用到这个数据,所以在这里初始化初始化
        equipmentList = equipmentService.queryEntityListAll();
    }
}

2、在启动类加上@EnableScheduling注解,到此定时任务类完成创建

package com.zyzj.site_civilization;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

}

官方文档参考:https://spring.io/guides/gs/scheduling-tasks/

然而,在我启动程序的时候出事了,我可是看着官方例子写的好不好,

出错信息如下:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-03-30 17:28:17.789 ERROR 17644 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'defaultSockJsTaskScheduler' is expected to be of type 'org.springframework.scheduling.TaskScheduler' but was actually of type 'org.springframework.beans.factory.support.NullBean'
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:392) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1115) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1082) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.resolveSchedulerBean(ScheduledAnnotationBeanPostProcessor.java:313) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:254) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:231) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:103) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:402) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:359) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:896) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:163) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at com.zyzj.site_civilization.SiteCivilizationApplication.main(SiteCivilizationApplication.java:12) [classes/:na]


Process finished with exit code 1

从报错信息上面看,是Bean named 'defaultSockJsTaskScheduler' is expected to be of type 'org.springframework.scheduling.TaskScheduler' but was actually of type 'org.springframework.beans.factory.support.NullBean',注入的类型不对,随后百度。

  然而百度里出现这样的问题多是重复引入导致包冲突或者是引入了错误的包,百思不得其解,我是哪里不对啊,我认真的对了一遍代码,没错啊。
就在这个时候,突然想起来一句话,说:当你在学习一个新东西新技术的时候,你只需要一个单纯的环境,避免一切可能和不可能的干扰,于是我新建一个项目,按照官网的示例代码写了一遍。好嘛,一点毛病也没有,那么问题肯定是定时任务和我现在环境有冲突,但是这个冲突会是什么导致的呢?
  我又看了一遍异常信息,突然发现,报错信息里面一个地方defaultSockJsTaskScheduler,SockJsTask,那么会不会是和websocket环境冲突了,测试了一下,将websocket去掉后,定时任务正常,终于找到你了。
之后感谢下面这篇博客,帮助我解决了问题
 
我的操作:
package com.jian.test.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class ScheduledConfig {

    @Bean
    public TaskScheduler taskScheduler(){
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.initialize();
        return taskScheduler;
    }
}

待验证:至于为什么会出现这样的问题,猜测可能是因为websocket也需要定时任务(心跳检测),定时任务其实也是依赖于一个新的线程,这些线程需要管理起来,需要线程池,websocket里面好像默认实现了一个ThreadPoolTaskScheduler,(ThreadPoolTaskScheduler实现了TaskScheduler接口),定时任务也需要线程池,可能这里面有什么冲突

 后记:最主要的问题,还是基础太差

原文地址:https://www.cnblogs.com/threadj/p/10638297.html