由于最近有个需求,产品即将到期(不同时间段到期)时给后台用户按角色推送,功能完成之后在此做个小结

1. 在启动类中添加注解@EnableScheduling

  1. package com.hsfw.backyard.websocket333;
  2.  
  3. /**
  4. * @Description
  5. * @Author: liucq
  6. * @Date: 2019/1/25
  7. */
  8.  
  9. import org.mybatis.spring.annotation.MapperScan;
  10. import org.springframework.boot.SpringApplication;
  11. import org.springframework.boot.autoconfigure.SpringBootApplication;
  12. import org.springframework.boot.builder.SpringApplicationBuilder;
  13. import org.springframework.boot.web.support.SpringBootServletInitializer;
  14. import org.springframework.scheduling.annotation.EnableScheduling;
  15.  
  16. @SpringBootApplication
  17. @MapperScan("com.siwei.insurance.*.dao")
  18. /**
  19. * //该注解是开启定时任务的支持
  20. */
  21. @EnableScheduling
  22. public class lifeInsuranceApplication extends SpringBootServletInitializer {
  23. @Override
  24. protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
  25. return builder.sources(lifeInsuranceApplication.class);
  26. }
  27.  
  28. public static void main(String[] args) {
  29. SpringApplication.run(lifeInsuranceApplication.class, args);
  30. }
  31. }

2. 写定时器

  1. package com.hsfw.backyard.websocket333;
  2.  
  3. /**
  4. * @Description
  5. * @Author: liucq
  6. * @Date: 2019/1/25
  7. */
  8.  
  9. import com.siwei.insurance.permission.dao.RolePermissionDao;
  10. import com.siwei.insurance.productManage.dao.ExpirePushMsgMapper;
  11. import com.siwei.insurance.productManage.service.ProductService;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.scheduling.annotation.Scheduled;
  14.  
  15. import java.util.ArrayList;
  16. import java.util.List;
  17.  
  18. @Componentpublic
  19. class ProductExpireTask {
  20. @Autowired
  21. private RolePermissionDao rolePermissionDao;
  22. @Autowired
  23. private ProductService productService;
  24. @Autowired
  25. private ExpirePushMsgMapper expirePushMsgMapper;
  26.  
  27. /**
  28. * 每天早上0点执行
  29. */
  30.  
  31. @Scheduled(cron = "0 0 0 1/1 * ?")
  32. public void productExpire() {
  33. //距离到期还有一个月提醒
  34. String oneMonthExpireDate = DateUtil.addOneMonth();
  35. dealExpireProduct(oneMonthExpireDate);
  36. //距离到期还有一天提醒
  37. String oneDayExpireDate = DateUtil.addOneDay();
  38. dealExpireProduct(oneDayExpireDate);
  39. //距离到期还有一周提醒
  40. String oneWeekExpireDate = DateUtil.addFewDays(7);
  41. dealExpireProduct(oneWeekExpireDate);
  42. }
  43.  
  44. private void dealExpireProduct(String expireDate) {
  45. List<Map<String, Object>> expireProductMapList = productService.findExpireProducts(expireDate);
  46. if (expireProductMapList != null && !expireProductMapList.isEmpty()) {
  47. // 根据路径查询需要推送的角色
  48. List<String> needPushRoleIds = rolePermissionDao.findNeedPushRoleIdByUrl(TotalConstant.PRODUCT_PUSH_URL);
  49. List<ExpirePushMsg> expirePushMsgs = new ArrayList<>();
  50. for (Map<String, Object> expireProductMap : expireProductMapList) {
  51. ExpirePushMsg expirePushMsg = new ExpirePushMsg();
  52. expirePushMsg.setNeedPushEntityId((int) expireProductMap.get("id"));
  53. expirePushMsg.setNeedPushEntityNo((String) expireProductMap.get("insuranceNo"));
  54. String productName = (String) expireProductMap.get("insuranceName");
  55. expirePushMsg.setNeedPushEntityName(productName);
  56. //设置此推送消息的到期时间
  57. expirePushMsg.setExpireDate(DateUtil.stringToDate(DateUtil.addOneDay()));
  58. expirePushMsg.setPushType(2);
  59. StringBuffer needPushRoleIdString = new StringBuffer();
  60. needPushRoleIds.forEach(e -> needPushRoleIdString.append(e + ";"));
  61. expirePushMsg.setPushRoleId(needPushRoleIdString.toString().trim());
  62. String productExpireDateString = DateUtil.dateToShotString((Date) expireProductMap.get("expiryDate"));
  63. expirePushMsg.setPushMsg("您的产品:" + productName + ",将于" + productExpireDateString + "即将过期,请及时处理!");
  64. expirePushMsgs.add(expirePushMsg);
  65. }
  66. expirePushMsgMapper.insertAll(expirePushMsgs);
  67. }
  68. }
  69.  
  70. @Scheduled(cron = "0 0 0 1/1 * ?")
  71. public void pushMsgExpire() {
  72. String oneDayExpireDate = DateUtil.getZeroTime(DateUtil.addOneDay());
  73. //推送消息只存在一天,根据到期时间将数据删除
  74. expirePushMsgMapper.deleteByExpireDate(oneDayExpireDate);
  75. }
  76. }

DateUtil工具类

  1. package com.hsfw.backyard.websocket333;
  2.  
  3. /**
  4. * @Description
  5. * @Author: liucq
  6. * @Date: 2019/1/25
  7. */
  8.  
  9. import org.apache.commons.lang3.StringUtils;
  10.  
  11. import java.text.ParsePosition;
  12. import java.text.SimpleDateFormat;
  13. import java.util.Calendar;
  14. import java.util.Date;
  15.  
  16. /**
  17. * @Description 时间处理工具类 * @author linxiunan * @date 2018年9月3日
  18. */
  19. public class DateUtil {
  20. private static final SimpleDateFormat dayOfDateFormat = new SimpleDateFormat("yyyy-MM-dd");
  21. private static final SimpleDateFormat secondOfDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  22.  
  23. /**
  24. * @return 当天时间加一天,返回"yyyy-MM-dd"格式
  25. */
  26. public static String addOneDay() {
  27. Calendar calendar = Calendar.getInstance();
  28. calendar.add(Calendar.DAY_OF_MONTH, 1);
  29. return dayOfDateFormat.format(calendar.getTime());
  30. }
  31.  
  32. /**
  33. * @return 当天时间加一月,返回"yyyy-MM-dd"格式
  34. */
  35. public static String addOneMonth() {
  36. Calendar calendar = Calendar.getInstance();
  37. calendar.add(Calendar.MONTH, 1);
  38. return dayOfDateFormat.format(calendar.getTime());
  39. }
  40.  
  41. /**
  42. * @param dayNumber 加的天数 * @return 返回当天时间添加几天之后的时间,返回"yyyy-MM-dd"格式
  43. */
  44. public static String addFewDays(int dayNumber) {
  45. Calendar calendar = Calendar.getInstance();
  46. calendar.add(Calendar.DAY_OF_MONTH, dayNumber);
  47. return dayOfDateFormat.format(calendar.getTime());
  48. }
  49.  
  50. /**
  51. * @param dateString 需要转换成时间格式的日期字符串 * @return 返回字符串转换成的时间
  52. */
  53. public static Date stringToDate(String dateString) {
  54. ParsePosition parsePosition = new ParsePosition(0);
  55. if (dateString.contains(" ")) {
  56. return secondOfDateFormat.parse(dateString, parsePosition);
  57. } else {
  58. return dayOfDateFormat.parse(dateString, parsePosition);
  59. }
  60. }
  61.  
  62. /**
  63. * @param date 需要转换成字符串格式的日期 * @return 返回"yyyy-MM-dd"格式的转换后的字符串
  64. */
  65. public static String dateToShotString(Date date) {
  66. return dayOfDateFormat.format(date);
  67. }
  68.  
  69. /**
  70. * @param date 需要转换成字符串格式的日期 * @return 返回"yyyy-MM-dd HH:mm:ss"格式的转换后的字符串
  71. */
  72. public static String dateToLongString(Date date) {
  73. return secondOfDateFormat.format(date);
  74. }
  75.  
  76. /**
  77. * @param dateString 需要获取0点的时间字符串,如果获取当天0点,传null即可 * @return 返回"yyyy-MM-dd HH:mm:ss"格式的某天0点字符串
  78. */
  79. public static String getZeroTime(String dateString) {
  80. if (StringUtils.isBlank(dateString)) {
  81. Calendar calendar = Calendar.getInstance();
  82. calendar.set(Calendar.HOUR_OF_DAY, 0);
  83. calendar.set(Calendar.MINUTE, 0);
  84. calendar.set(Calendar.SECOND, 0);
  85. return secondOfDateFormat.format(calendar.getTime());
  86. } else {
  87. Date date = stringToDate(dateString);
  88. return dateToLongString(date);
  89. }
  90. }
  91. }

3. 引入websocket所需jar包

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>

4. 配置websocket

编写MyEndpointConfigure类

  1. package com.hsfw.backyard.websocket333;
  2.  
  3. /**
  4. * @Description
  5. * @Author: liucq
  6. * @Date: 2019/1/25
  7. */
  8.  
  9. import org.springframework.beans.BeansException;
  10. import org.springframework.beans.factory.BeanFactory;
  11. import org.springframework.context.ApplicationContext;
  12. import org.springframework.context.ApplicationContextAware;
  13.  
  14. import javax.websocket.server.ServerEndpointConfig;
  15.  
  16. public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
  17. private static volatile BeanFactory context;
  18.  
  19. @Override
  20. public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
  21. return context.getBean(clazz);
  22. }
  23.  
  24. @Override
  25. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  26. MyEndpointConfigure.context = applicationContext;
  27. }
  28. }

websocket配置类

  1. package com.hsfw.backyard.websocket333;
  2.  
  3. /**
  4. * @Description
  5. * @Author: liucq
  6. * @Date: 2019/1/25
  7. */
  8.  
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  12.  
  13. @Configuration
  14. public class WebSocketConfig {
  15. @Bean
  16. public ServerEndpointExporter serverEndpointExporter() {
  17. return new ServerEndpointExporter();
  18. }
  19.  
  20. @Bean
  21. public MyEndpointConfigure newConfigure() {
  22. return new MyEndpointConfigure();
  23. }
  24. }

这里需要重点说明一下,在websocket配置类中,第一个配置是因为使用springboot内置容器,自己开发时需要配置,如果有独立的容器需要将其注释掉,也就意味着,如果将项目打成WAR包,部署到服务器,使用Tomcat启动时,需要注释掉ServerEndpointExporter配置;MyEndpointConfigure配置是因为我的需求需要,需要在websocket类中注入service层或者dao层的接口,MyEndpointConfigure配置就是为了解决websocket无法注入的问题,如果没有需要可以不用配置
---------------------

5. websocket类

  1. package com.hsfw.backyard.websocket333;
  2.  
  3. /**
  4. * @Description
  5. * @Author: liucq
  6. * @Date: 2019/1/25
  7. */
  8.  
  9. import org.apache.commons.lang.StringUtils;
  10. import org.slf4j.Logger;
  11. import org.slf4j.LoggerFactory;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.stereotype.Component;
  14.  
  15. import javax.websocket.OnClose;
  16. import javax.websocket.OnError;
  17. import javax.websocket.OnMessage;
  18. import javax.websocket.OnOpen;
  19. import javax.websocket.server.PathParam;
  20. import javax.websocket.server.ServerEndpoint;
  21. import java.io.IOException;
  22. import java.util.ArrayList;
  23. import java.util.List;
  24. import java.util.concurrent.CopyOnWriteArraySet;
  25.  
  26. @Component
  27. @ServerEndpoint(value = "/productWebSocket/{userId}", configurator = MyEndpointConfigure.class)
  28. public class ProductWebSocket {
  29. // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
  30. private static int onlineCount = 0;
  31. // concurrent包的线程安全Set,用来存放每个客户端对应的ProductWebSocket对象。
  32. private static CopyOnWriteArraySet<ProductWebSocket> webSocketSet = new CopyOnWriteArraySet<ProductWebSocket>();
  33. // 与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session;
  34. @Autowired
  35. private UserRoleDao userRoleDao;
  36. @Autowired
  37. private ExpirePushMsgMapper expirePushMsgMapper;
  38. private Logger log = LoggerFactory.getLogger(ProductWebSocket.class);
  39.  
  40. /**
  41. * 连接建立成功调用的方法
  42. */
  43. @OnOpen
  44. public void onOpen(@PathParam("userId") String userId, Session session) {
  45. log.info("新客户端连入,用户id:" + userId);
  46. this.session = session;
  47. webSocketSet.add(this); // 加入set中
  48. addOnlineCount(); // 在线数加1
  49. // 相关业务处理,根据拿到的用户ID判断其为那种角色,根据角色ID去查询是否有需要推送给该角色的消息,有则推送
  50. if (StringUtils.isNotBlank(userId)) {
  51. List<String> roleIds = userRoleDao.findRoleIdByUserId(userId);
  52. List<String> totalPushMsgs = new ArrayList<String>();
  53. for (String roleId : roleIds) {
  54. List<String> pushMsgs = expirePushMsgMapper.findPushMsgByRoleId(roleId);
  55. if (pushMsgs != null && !pushMsgs.isEmpty()) {
  56. totalPushMsgs.addAll(pushMsgs);
  57. }
  58. }
  59. if (totalPushMsgs != null && !totalPushMsgs.isEmpty()) {
  60. totalPushMsgs.forEach(e -> sendMessage(e));
  61. }
  62. }
  63. }
  64.  
  65. /**
  66. * 连接关闭调用的方法
  67. */
  68. @OnClose
  69. public void onClose() {
  70. log.info("一个客户端关闭连接");
  71. webSocketSet.remove(this); // 从set中删除
  72. subOnlineCount(); // 在线数减1
  73. }
  74.  
  75. /**
  76. * 收到客户端消息后调用的方法
  77. * * @param message 客户端发送过来的消息
  78. */
  79. @OnMessage
  80. public void onMessage(String message, Session session) {
  81. }
  82.  
  83. /**
  84. * 发生错误时调用
  85. */
  86. @OnError
  87. public void onError(Session session, Throwable error) {
  88. log.error("websocket出现错误");
  89. error.printStackTrace();
  90. }
  91.  
  92. public void sendMessage(String message) {
  93. try {
  94. this.session.getBasicRemote().sendText(message);
  95. log.info("推送消息成功,消息为:" + message);
  96. } catch (IOException e) {
  97. e.printStackTrace();
  98. }
  99. }
  100.  
  101. /**
  102. * 群发自定义消息
  103. */
  104. public static void sendInfo(String message) throws IOException {
  105. for (ProductWebSocket productWebSocket : webSocketSet) {
  106. productWebSocket.sendMessage(message);
  107. }
  108. }
  109.  
  110. public static synchronized int getOnlineCount() {
  111. return onlineCount;
  112. }
  113.  
  114. public static synchronized void addOnlineCount() {
  115. ProductWebSocket.onlineCount++;
  116. }
  117.  
  118. public static synchronized void subOnlineCount() {
  119. ProductWebSocket.onlineCount--;
  120. }
  121. }

这样后台的功能基本上就算是写完了,前端配合测试一下

6. 前端测试

写一个页面,代码如下

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <title>My WebSocket</title>
  5. </head>
  6.  
  7. <body>
  8. Welcome<br/>
  9. <input id="text" type="text" /><button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button>
  10. <div id="message">
  11. </div>
  12. </body>
  13.  
  14. <script type="text/javascript">
  15. var websocket = null;
  16.  
  17. //判断当前浏览器是否支持WebSocket
  18. if('WebSocket' in window){
  19. websocket = new WebSocket("ws://localhost:8080/productWebSocket/001");
  20. }
  21. else{
  22. alert('Not support websocket')
  23. }
  24.  
  25. //连接发生错误的回调方法
  26. websocket.onerror = function(){
  27. setMessageInnerHTML("error");
  28. };
  29.  
  30. //连接成功建立的回调方法
  31. websocket.onopen = function(event){
  32. setMessageInnerHTML("open");
  33. }
  34.  
  35. //接收到消息的回调方法
  36. websocket.onmessage = function(event){
  37. setMessageInnerHTML(event.data);
  38. }
  39.  
  40. //连接关闭的回调方法
  41. websocket.onclose = function(){
  42. setMessageInnerHTML("close");
  43. }
  44.  
  45. //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
  46. window.onbeforeunload = function(){
  47. websocket.close();
  48. }
  49.  
  50. //将消息显示在网页上
  51. function setMessageInnerHTML(innerHTML){
  52. document.getElementById('message').innerHTML += innerHTML + '<br/>';
  53. }
  54.  
  55. //关闭连接
  56. function closeWebSocket(){
  57. websocket.close();
  58. }
  59.  
  60. //发送消息
  61. function send(){
  62. var message = document.getElementById('text').value;
  63. websocket.send(message);
  64. }
  65. </script>
  66. </html>

项目启动之后,打开页面

open下方就是我添加的消息,可以看出已经成功推送,到此该功能就算完成结束了

参考地址:https://www.cnblogs.com/bianzy/p/5822426.html

Springboot+websocket+定时器实现消息推送的更多相关文章

  1. springboot+websocket+sockjs进行消息推送【基于STOMP协议】

    springboot+websocket+sockjs进行消息推送[基于STOMP协议] WebSocket是在HTML5基础上单个TCP连接上进行全双工通讯的协议,只要浏览器和服务器进行一次握手,就 ...

  2. springboot整合websocket实现一对一消息推送和广播消息推送

    maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  3. 拾人牙慧篇之——基于HTML5中websocket来实现消息推送功能

    一.写在前面 要求做一个,后台发布信息,前台能即时得到通知的消息推送功能.网上搜了也有很多方式,ajax的定时询问,Comet方式,Server-Sent方式,以及websocket.表示除了定时询问 ...

  4. 简易集成websocket技术实现消息推送

    Websocket 简介 首先介绍下WebSocket,它是一种网络通信技术,该技术最大的特点就是,服务器端可以主动往客户端发送消息:当然,客户端也可以主动往服务器发送消息,实现两端的消息通信,属于网 ...

  5. python websocket Django 实时消息推送

    概述: WebSocket 是什么? WebSocket 是 HTML5 提供的一种浏览器与服务器间进行全双工通讯的协议.依靠这种协议可以实现客户端和服务器端 ,一次握手,双向实时通信. WebSoc ...

  6. WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  7. HTML5 学习总结(五)——WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  8. HTML5 学习笔记(五)——WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  9. 使用websocket进行消息推送服务

    Websocket主要做消息推送,简单,轻巧,比comet好用 入门了解:https://www.cnblogs.com/xdp-gacl/p/5193279.html /** * A Web Soc ...

随机推荐

  1. 加载UI工程的csb,以及纹理缓存情况

    以plist+PNG模式加载csb,并播放UI工程做的动画,用法如下 local Layer = cc.CSLoader:createNode("res/yk/interface/loadi ...

  2. Python-Pool类

    目录: multiprocessing模块 Pool类 apply apply_async map close terminate join 进程实例 multiprocessing模块 如果你打算编 ...

  3. 清北学堂 清北-Day1-R2-监听monitor

    题目描述 [背景] 不阅读本题的[背景]并不影响通过本题. 三体信息中没有包含对三体⼈⽣物形态的任何描述,⼈类要在四百多年以后才能真正看到三体⼈.在阅读信息时,叶⽂洁只能把三体⼈想象成⼈类的形象. 1 ...

  4. 【Java】「深入理解Java虚拟机」学习笔记(4)- 类文件结构

    我为什么喜欢Java,另一个重要原因就是跨平台,WORA. 程序员是爽了,但肯定有人要为你遮风挡雨,解决WORA的基石就是字节码+虚拟机. ♣Tip 其实这里存在两种无关性,一是平台无关性.另一个是语 ...

  5. SpringCloud服务提供者

    服务提供者就是提供一个服务暴露出来给别人调用,在springcloud中需要注册服务到服务中心 搭建服务提供者项目(ProduceDemo) 1.创建pom.xml <project xmlns ...

  6. Memcached常用语法与java连接服务

    memcached常用语法及java使用方式 Author:SimpleWu Memcached 存储命令 Memcached set 命令用于将 value(数据值) 存储在指定的 key(键) 中 ...

  7. django之ORM数据库操作

    一.ORM介绍 映射关系: 表名 -------------------->类名 字段-------------------->属性 表记录----------------->类实例 ...

  8. Op-level的快速算法

    十岁的小男孩 本文为终端移植的一个小章节. 目录 引言 FFT Conv2d (7x7, 9x9) Winograd Conv2d (3x3, 5x5) 引言 本节针对CNN进行加速计算的,主要有以下 ...

  9. cf1133 bcdef

    b所有数模k,记录出现次数即可 #include<bits/stdc++.h> using namespace std; int main(){ ]; ]={}; cin>>n ...

  10. php url函数

    1.base64_encode 与 base64_decode base64_encode(string) 表示使用 MIME base64 对数据进行编码 base64_decode(string) ...