java定时器推送消息_Springboot+websocket+定时器实现消息推送

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

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

packagecom.hsfw.backyard.websocket333;/*** @Description

* @Author: liucq

* @Date: 2019/1/25*/

importorg.mybatis.spring.annotation.MapperScan;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.boot.builder.SpringApplicationBuilder;importorg.springframework.boot.web.support.SpringBootServletInitializer;importorg.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication

@MapperScan("com.siwei.insurance.*.dao")/*** //该注解是开启定时任务的支持*/@EnableSchedulingpublic class lifeInsuranceApplication extendsSpringBootServletInitializer {

@OverrideprotectedSpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(lifeInsuranceApplication.class);

}public static voidmain(String[] args) {

SpringApplication.run(lifeInsuranceApplication.class, args);

}

}

2. 写定时器

packagecom.hsfw.backyard.websocket333;/*** @Description

* @Author: liucq

* @Date: 2019/1/25*/

importcom.siwei.insurance.permission.dao.RolePermissionDao;importcom.siwei.insurance.productManage.dao.ExpirePushMsgMapper;importcom.siwei.insurance.productManage.service.ProductService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.scheduling.annotation.Scheduled;importjava.util.ArrayList;importjava.util.List;

@ComponentpublicclassProductExpireTask {

@AutowiredprivateRolePermissionDao rolePermissionDao;

@AutowiredprivateProductService productService;

@AutowiredprivateExpirePushMsgMapper expirePushMsgMapper;/*** 每天早上0点执行*/@Scheduled(cron= "0 0 0 1/1 * ?")public voidproductExpire() {//距离到期还有一个月提醒

String oneMonthExpireDate =DateUtil.addOneMonth();

dealExpireProduct(oneMonthExpireDate);//距离到期还有一天提醒

String oneDayExpireDate =DateUtil.addOneDay();

dealExpireProduct(oneDayExpireDate);//距离到期还有一周提醒

String oneWeekExpireDate = DateUtil.addFewDays(7);

dealExpireProduct(oneWeekExpireDate);

}private voiddealExpireProduct(String expireDate) {

List> expireProductMapList =productService.findExpireProducts(expireDate);if (expireProductMapList != null && !expireProductMapList.isEmpty()) {//根据路径查询需要推送的角色

List needPushRoleIds =rolePermissionDao.findNeedPushRoleIdByUrl(TotalConstant.PRODUCT_PUSH_URL);

List expirePushMsgs = new ArrayList<>();for (MapexpireProductMap : expireProductMapList) {

ExpirePushMsg expirePushMsg= newExpirePushMsg();

expirePushMsg.setNeedPushEntityId((int) expireProductMap.get("id"));

expirePushMsg.setNeedPushEntityNo((String) expireProductMap.get("insuranceNo"));

String productName= (String) expireProductMap.get("insuranceName");

expirePushMsg.setNeedPushEntityName(productName);//设置此推送消息的到期时间

expirePushMsg.setExpireDate(DateUtil.stringToDate(DateUtil.addOneDay()));

expirePushMsg.setPushType(2);

StringBuffer needPushRoleIdString= newStringBuffer();

needPushRoleIds.forEach(e-> needPushRoleIdString.append(e + ";"));

expirePushMsg.setPushRoleId(needPushRoleIdString.toString().trim());

String productExpireDateString= DateUtil.dateToShotString((Date) expireProductMap.get("expiryDate"));

expirePushMsg.setPushMsg("您的产品:" + productName + ",将于" + productExpireDateString + "即将过期,请及时处理!");

expirePushMsgs.add(expirePushMsg);

}

expirePushMsgMapper.insertAll(expirePushMsgs);

}

}

@Scheduled(cron= "0 0 0 1/1 * ?")public voidpushMsgExpire() {

String oneDayExpireDate=DateUtil.getZeroTime(DateUtil.addOneDay());//推送消息只存在一天,根据到期时间将数据删除

expirePushMsgMapper.deleteByExpireDate(oneDayExpireDate);

}

}

DateUtil工具类

packagecom.hsfw.backyard.websocket333;/*** @Description

* @Author: liucq

* @Date: 2019/1/25*/

importorg.apache.commons.lang3.StringUtils;importjava.text.ParsePosition;importjava.text.SimpleDateFormat;importjava.util.Calendar;importjava.util.Date;/*** @Description 时间处理工具类 *@authorlinxiunan * @date 2018年9月3日*/

public classDateUtil {private static final SimpleDateFormat dayOfDateFormat = new SimpleDateFormat("yyyy-MM-dd");private static final SimpleDateFormat secondOfDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");/***@return当天时间加一天,返回"yyyy-MM-dd"格式*/

public staticString addOneDay() {

Calendar calendar=Calendar.getInstance();

calendar.add(Calendar.DAY_OF_MONTH,1);returndayOfDateFormat.format(calendar.getTime());

}/***@return当天时间加一月,返回"yyyy-MM-dd"格式*/

public staticString addOneMonth() {

Calendar calendar=Calendar.getInstance();

calendar.add(Calendar.MONTH,1);returndayOfDateFormat.format(calendar.getTime());

}/***@paramdayNumber 加的天数 *@return返回当天时间添加几天之后的时间,返回"yyyy-MM-dd"格式*/

public static String addFewDays(intdayNumber) {

Calendar calendar=Calendar.getInstance();

calendar.add(Calendar.DAY_OF_MONTH, dayNumber);returndayOfDateFormat.format(calendar.getTime());

}/***@paramdateString 需要转换成时间格式的日期字符串 *@return返回字符串转换成的时间*/

public staticDate stringToDate(String dateString) {

ParsePosition parsePosition= new ParsePosition(0);if (dateString.contains(" ")) {returnsecondOfDateFormat.parse(dateString, parsePosition);

}else{returndayOfDateFormat.parse(dateString, parsePosition);

}

}/***@paramdate 需要转换成字符串格式的日期 *@return返回"yyyy-MM-dd"格式的转换后的字符串*/

public staticString dateToShotString(Date date) {returndayOfDateFormat.format(date);

}/***@paramdate 需要转换成字符串格式的日期 *@return返回"yyyy-MM-dd HH:mm:ss"格式的转换后的字符串*/

public staticString dateToLongString(Date date) {returnsecondOfDateFormat.format(date);

}/***@paramdateString 需要获取0点的时间字符串,如果获取当天0点,传null即可 *@return返回"yyyy-MM-dd HH:mm:ss"格式的某天0点字符串*/

public staticString getZeroTime(String dateString) {if(StringUtils.isBlank(dateString)) {

Calendar calendar=Calendar.getInstance();

calendar.set(Calendar.HOUR_OF_DAY,0);

calendar.set(Calendar.MINUTE,0);

calendar.set(Calendar.SECOND,0);returnsecondOfDateFormat.format(calendar.getTime());

}else{

Date date=stringToDate(dateString);returndateToLongString(date);

}

}

}

3. 引入websocket所需jar包

org.springframework.boot

spring-boot-starter-websocket

4. 配置websocket

编写MyEndpointConfigure类

packagecom.hsfw.backyard.websocket333;/*** @Description

* @Author: liucq

* @Date: 2019/1/25*/

importorg.springframework.beans.BeansException;importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importjavax.websocket.server.ServerEndpointConfig;public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implementsApplicationContextAware {private static volatileBeanFactory context;

@Overridepublic T getEndpointInstance(Class clazz) throwsInstantiationException {returncontext.getBean(clazz);

}

@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throwsBeansException {

MyEndpointConfigure.context=applicationContext;

}

}

websocket配置类

packagecom.hsfw.backyard.websocket333;/*** @Description

* @Author: liucq

* @Date: 2019/1/25*/

importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configurationpublic classWebSocketConfig {

@BeanpublicServerEndpointExporter serverEndpointExporter() {return newServerEndpointExporter();

}

@BeanpublicMyEndpointConfigure newConfigure() {return newMyEndpointConfigure();

}

}

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

---------------------

5. websocket类

packagecom.hsfw.backyard.websocket333;/*** @Description

* @Author: liucq

* @Date: 2019/1/25*/

importorg.apache.commons.lang.StringUtils;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importjavax.websocket.OnClose;importjavax.websocket.OnError;importjavax.websocket.OnMessage;importjavax.websocket.OnOpen;importjavax.websocket.server.PathParam;importjavax.websocket.server.ServerEndpoint;importjava.io.IOException;importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.CopyOnWriteArraySet;

@Component

@ServerEndpoint(value= "/productWebSocket/{userId}", configurator = MyEndpointConfigure.class)public classProductWebSocket {//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。

private static int onlineCount = 0;//concurrent包的线程安全Set,用来存放每个客户端对应的ProductWebSocket对象。

private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();//与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session;

@AutowiredprivateUserRoleDao userRoleDao;

@AutowiredprivateExpirePushMsgMapper expirePushMsgMapper;private Logger log = LoggerFactory.getLogger(ProductWebSocket.class);/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(@PathParam("userId") String userId, Session session) {

log.info("新客户端连入,用户id:" +userId);this.session =session;

webSocketSet.add(this); //加入set中

addOnlineCount(); //在线数加1//相关业务处理,根据拿到的用户ID判断其为那种角色,根据角色ID去查询是否有需要推送给该角色的消息,有则推送

if(StringUtils.isNotBlank(userId)) {

List roleIds =userRoleDao.findRoleIdByUserId(userId);

List totalPushMsgs = new ArrayList();for(String roleId : roleIds) {

List pushMsgs =expirePushMsgMapper.findPushMsgByRoleId(roleId);if (pushMsgs != null && !pushMsgs.isEmpty()) {

totalPushMsgs.addAll(pushMsgs);

}

}if (totalPushMsgs != null && !totalPushMsgs.isEmpty()) {

totalPushMsgs.forEach(e->sendMessage(e));

}

}

}/*** 连接关闭调用的方法*/@OnClosepublic voidonClose() {

log.info("一个客户端关闭连接");

webSocketSet.remove(this); //从set中删除

subOnlineCount(); //在线数减1

}/*** 收到客户端消息后调用的方法

* *@parammessage 客户端发送过来的消息*/@OnMessagepublic voidonMessage(String message, Session session) {

}/*** 发生错误时调用*/@OnErrorpublic voidonError(Session session, Throwable error) {

log.error("websocket出现错误");

error.printStackTrace();

}public voidsendMessage(String message) {try{this.session.getBasicRemote().sendText(message);

log.info("推送消息成功,消息为:" +message);

}catch(IOException e) {

e.printStackTrace();

}

}/*** 群发自定义消息*/

public static void sendInfo(String message) throwsIOException {for(ProductWebSocket productWebSocket : webSocketSet) {

productWebSocket.sendMessage(message);

}

}public static synchronized intgetOnlineCount() {returnonlineCount;

}public static synchronized voidaddOnlineCount() {

ProductWebSocket.onlineCount++;

}public static synchronized voidsubOnlineCount() {

ProductWebSocket.onlineCount--;

}

}

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

6. 前端测试

写一个页面,代码如下

My WebSocket

Send Close

你可能感兴趣的:(java定时器推送消息)