Spring Task,WebSocket
Spring Task
介绍:
Spring Task是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑;
定位:定时任务框架
作用:定时自动执行某段Java代码;
应用场景:
信用卡每月还款提醒;
银行贷款每月还款提醒;
火车票售票系统处理未支付订单;
入职纪念日为用户发送通知;
cron表达式:
介绍:
cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间;
构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义;
每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选);
各字段含义:
字段 | 允许值 | 允许的特殊字符 |
---|---|---|
秒(Seconds) | 0~59的整数 | , – * / 四个字符 |
分(Minutes) | 0~59的整数 | , – * / 四个字符 |
小时(Hours) | 0~23的整数 | , – * / 四个字符 |
日期(DayofMonth) | 1~31的整数(但是你需要考虑你月的天数) | , – * ? / L W C 八个字符 |
月份(Month) | 1~12的整数或者 JAN-DEC | , – * / 四个字符 |
星期(DayofWeek) | 1~7的整数或者 SUN-SAT(1=SUM) | , – * ? / L C # 八个字符 |
年(可选,留空)(Year) | 1970~2099 | , – * / 四个字符 |
特殊字符含义
* :表示匹配该域的任意值。假如在Minutes域使用,即表示每分钟都会触发事件。
?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法:1313 15 20*?,其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。
:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着第5分钟触发一次,之后每隔20分钟触发一次;
,:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。
L:表示最后,只能出现在DayofWeek和DayofMonth域。在DayofMonth域使用L,表示最后一天;如果在DayofWeek域使用5L,意味着在最后的一个星期五触发。
W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份。
LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第四个星期二,第四周的星期二。
例子:
秒 | 分钟 | 小时 | 日 | 月 | 周 | 年 |
---|---|---|---|---|---|---|
0 | 0 | 9 | 12 | 10 | ? | 2022 |
2022年10月12日上午9点整 对应的表达式为:0 0 9 12 10 ? 2022
在线工具:
在线Cron表达式生成器 (qqe2.com)
使用步骤:
导入maven坐标 spring-context(已存在),没有属于自己的jar包,集成在spring-context中;
启动类添加注解 @EnableScheduling 开启任务调度;
自定义定时任务类:
类中添加方法,方法添加 @Scheduled 注解设置定时执行;
注解参数为 cron表达式;
自定义定时任务类:
@Component
@Slf4j
public class MyTask {
// 定时任务 每隔5秒触发一次
@Scheduled(cron = "0/5 * * * * ?")
public void executeTask(){
log.info("定时任务开始执行");
}
}
WebSocket
介绍:
简介:
WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输;
HTTP协议和WebSocket协议对比:
HTTP是短连接;
WebSocket是长连接;
HTTP通信是单向的,基于请求响应模式;
WebSocket支持双向通信;
HTTP和WebSocket底层都是TCP连接;
应用场景:
视频弹幕;
网页聊天;
体育实况更新;
股票基金报价实时更新;
后端实施:
简介:
导入WebSocket的maven坐标;
创建一个配置类,注册WebSocket的服务端组件;
创建WebSocket服务端组件自定义类,用于和客户端通信;
导入依赖:
在pom.xml中导入WebSocket的maven坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</<artifactId>>
</dependency>
配置类开启对WebSocket的支持:
/**
*注册WebSocket的服务端组件
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
WebSocket服务端, 用于与客户端通信:
添加注解会自动回调:
session
: 在WebSocket上下文中,session
对象代表了一个打开的WebSocket会话。这个对象提供了与远程端点通信的方法和属性。
getBasicRemote()
: 这是Session
对象的一个方法,它返回一个RemoteEndpoint.Basic
对象。RemoteEndpoint.Basic
是一个接口,它提供了发送文本消息、二进制消息以及关闭连接等方法。
sendText(message)
: 这是RemoteEndpoint.Basic
接口的一个方法,它用于向远程端点发送一个文本消息。参数message
是一个字符串,代表了要发送的消息内容。
package com.sky.websocket;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* WebSocket服务
*/
@Component
// 定义WebSocket服务器端点的访问路径
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
//存放会话对象
private static Map<String, Session> sessionMap = new HashMap();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
System.out.println("客户端:" + sid + "建立连接");
sessionMap.put(sid, session);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}
/**
* 连接关闭调用的方法
*
* @param sid
*/
@OnClose
public void onClose(@PathParam("sid") String sid) {
System.out.println("连接断开:" + sid);
sessionMap.remove(sid);
}
/**
* 群发
*
* @param message
*/
public void sendToAllClient(String message) {
Collection<Session> sessions = sessionMap.values();
for (Session session : sessions) {
try {
//服务器向客户端发送消息
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
前端实施:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Demo</title>
</head>
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息</button>
<button onclick="closeWebSocket()">关闭连接</button>
<div id="message">
</div>
</body>
<script type="text/javascript">
var websocket = null;
// 生成随机id,防止重复
var clientId = Math.random().toString(36).substr(2);
// 判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
// 连接WebSocket节点
websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);
}
else{
alert('Not support websocket')
}
// 连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
// 连接成功建立的回调方法
websocket.onopen = function(){
setMessageInnerHTML("连接成功");
}
// 接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
// 连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
}
// 将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
// 发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
// 关闭连接
function closeWebSocket() {
websocket.close();
}
</script>
</html>