文章目录
- 前言: 只是做笔记.-不够明白,故.不做解释
- 参考博客
- 源码下载地址:
- 目录结构
- config
- MyEndpointConfigure
- WebSocketConfiguration
- push
- ProductExpireTask
- socket
- ProductWebSocket
- util
- SpringUtil
- TimeConversionUtil
- config
- application.properties
- application-dev.properties
- templates
- index.html
- 启动类:HuawenCloudStockApplication
- http://localhost:8080/
前言: 只是做笔记.-不够明白,故.不做解释
参考博客
参考
源码下载地址:
链接:https://pan.baidu.com/s/1wNqE-Auba8sZuf129QazEg
提取码:cs6s
目录结构
config
MyEndpointConfigure
package com.hwqh.huawencloudstock.config;
import com.hwqh.huawencloudstock.util.SpringUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import javax.websocket.server.ServerEndpointConfig;
public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
private static ApplicationContext applicationContext = SpringUtil.getApplicationContext();
/**
* 3
* @param clazz
* @param <T>
* @return
*/
@Override
public <T> T getEndpointInstance(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
MyEndpointConfigure.applicationContext = applicationContext;
}
}
WebSocketConfiguration
package com.hwqh.huawencloudstock.config;
import com.hwqh.huawencloudstock.socket.ProductWebSocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* 首先访问的
*/
@Configuration
public class WebSocketConfiguration {
private Logger log = LoggerFactory.getLogger(ProductWebSocket.class);
/**
* ServerEndpointExporter 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
push
ProductExpireTask
package com.hwqh.huawencloudstock.push;
import com.hwqh.huawencloudstock.socket.ProductWebSocket;
import com.hwqh.huawencloudstock.util.TimeConversionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class ProductExpireTask {
private Logger log = LoggerFactory.getLogger(ProductWebSocket.class);
@Scheduled(fixedRate=2000)
public void productExpire() throws IOException {
String[] strs={/*"Test随机消息 :30.1123",*/
/* "Test随机消息 :32.1021",
"Test随机消息 :33.1774",
"Test随机消息 :33.2372",
"Test随机消息 :31.0281",
"Test随机消息 :30.0222",
"Test随机消息 :32.1322",
"Test随机消息 :33.3221",
"Test随机消息 :31.2311",*/
"Test随机消息 :32.3112"};
ProductWebSocket.sendInfo(TimeConversionUtil.getTimeStrNow()+" Test 消息---->"+ RandomStr(strs));
}
//随机返回字符串数组中的字符串
public static String RandomStr(String[] strs){
int random_index = (int) (Math.random()*strs.length);
return strs[random_index];
}
}
socket
ProductWebSocket
package com.hwqh.huawencloudstock.socket;
import com.hwqh.huawencloudstock.config.MyEndpointConfigure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Entry;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
@Component
@ServerEndpoint(value = "/productWebSocket/{userId}", configurator = MyEndpointConfigure.class)
public class ProductWebSocket {
private Logger log = LoggerFactory.getLogger(ProductWebSocket.class);
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
*/
private static final AtomicInteger OnlineCount = new AtomicInteger(0);
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的ProductWebSocket对象。
*/
private static CopyOnWriteArrayList<ProductWebSocket> webSocketSet = new CopyOnWriteArrayList<ProductWebSocket>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(@PathParam("userId") String userId, Session session) {
log.info("新客户端连入,用户id:" + userId);
this.session = session;
webSocketSet.add(this); // 加入set中
addOnlineCount(); // 在线数加1
if (userId != null) {
List<String> totalPushMsgs = new ArrayList<String>();
totalPushMsgs.add(userId + "连接成功-" + "-当前在线人数为:" + getOnlineCount());
if (totalPushMsgs != null && !totalPushMsgs.isEmpty()) {
totalPushMsgs.forEach(e -> sendMessage(e));
}
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
log.info("一个客户端关闭连接");
webSocketSet.remove(this); // 从set中删除
subOnlineCount(); // 在线数减1
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("用户发送过来的消息为:" + message);
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("websocket出现错误");
error.printStackTrace();
}
public void sendMessage(String message) {
this.session.getAsyncRemote().sendText(message);
log.info("推送消息成功,消息为:" + message);
}
/**
* 群发自定义消息
*/
/* public static void sendInfo(String message) {
for (ProductWebSocket productWebSocket : webSocketSet) {
productWebSocket.sendMessage(message);
}
}
*/
/**
* 向所有连接客户端推送消息
*/
public static void sendInfo(String message) {
if (webSocketSet.size() != 0) {
for (ProductWebSocket p : webSocketSet) {
if (p != null) {
p.sendMessage(message);
}
}
}
}
public static synchronized int getOnlineCount() {
return OnlineCount.get();
}
public static synchronized void addOnlineCount() {
OnlineCount.incrementAndGet(); // 在线数加1
}
public static synchronized void subOnlineCount() {
OnlineCount.decrementAndGet(); // 在线数加1
}
}
util
SpringUtil
package com.hwqh.huawencloudstock.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 说明:Spring获取Bean工具类
*/
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
TimeConversionUtil
package com.hwqh.huawencloudstock.util;
import org.springframework.util.Assert;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class TimeConversionUtil {
//传入long时间戳 ,转换成格式化的String类型时间
public String TimeOfLongToStr(long time) {
Assert.notNull(time, "time is null");
DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault()));
}
//传入String类型格式化时间,转换成Long类型的时间戳
public long TimeOfStrToLong(String strTime){
Assert.notNull(strTime, "time is null");
DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime parse = LocalDateTime.parse(strTime, ftf);
return LocalDateTime.from(parse).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
//通过LocalDateTime获取当前格式化时间
public static String getTimeStrNow(){
//DateTimeFormatter替换了SimpleDateFormat, 因为线程安全问题。
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
//通过LocalDateTime获取当前格式化时间
public static String getTimeStrNow1(){
//DateTimeFormatter替换了SimpleDateFormat, 因为线程安全问题。
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
//获取当前时间戳方法一
public long getTimeLongOfClock(){
return Clock.systemDefaultZone().millis();
}
//获取当前时间戳方法二
public long getTimeLongOfCalendar(){
Calendar calendar = Calendar.getInstance();
return calendar.getTimeInMillis();
}
//获取当前时间戳方法三
public long getTimeLongOfSystem(){
return System.currentTimeMillis();
}
//获取当前时间戳方法四
public long getTimeLongOfDate(){
Date date = new Date();
return date.getTime();
}
//获取当前时间戳五
public long getTimeLongOfInstant(){
Instant now = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8));
// System.out.println("秒数:"+now.getEpochSecond());
// System.out.println("毫秒数:"+now.toEpochMilli());
return now.toEpochMilli();
}
工具类调试
// public static void main(String[] args) {
TimeConversionUtil timeConversionUtil=new TimeConversionUtil();
System.out.println(timeConversionUtil.getTimeLongOfCalendar());
System.out.println(timeConversionUtil.getTimeLongOfDate());
System.out.println(timeConversionUtil.getTimeLongOfInstant());
System.out.println(timeConversionUtil.TimeOfLongToStr(timeConversionUtil.getTimeLongOfDate()));
System.out.println(TimeConversionUtil.getTimeStrNow());
// String a=null;
// Assert.notNull(a,"a是空的");
//
// }
}
config
application.properties
server.port=8080
# 是否使用缓存,开发阶段最填false,方便使用ctrl+shift+F9 进行重新编译,无需重启服务
spring.thymeleaf.cache=false
# 检查该模板是否存在
spring.thymeleaf.check-template-location=true
# 启动 MVC 对Thymeleaf 视图的解析
spring.thymeleaf.enabled=true
# 模板的字符集
spring.thymeleaf.encoding=UTF-8
# 从解析中排除的视图名称的逗号分隔列表,没有的话就空咯
spring.thymeleaf.excluded-view-names=
# 使用的是什么类型模板
spring.thymeleaf.mode=HTML5
# 在构建URL时可以预览查看名称的前缀。就是路径在哪
spring.thymeleaf.prefix=classpath:/templates/
spring.resources.static-locations=classpath:/templates/
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.html
spring.mvc.servlet.load-on-startup=100
#测试
spring.profiles.active=dev
#线上测试
#spring.profiles.active=test
application-dev.properties
#-----------------------------mysql-----------------------------------
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
templates
index.html
<!DOCTYPE HTML>
<html>
<head>
<title>Test My WebSocket</title>
</head>
<body>
Test<br/>
<input id="text" type="text"/>
<button onclick="send()">Send</button>
<button onclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>
<script type="text/javascript">
var websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
//连接WebSocket节点
websocket = new WebSocket("ws://localhost:8080/productWebSocket/001");
} else {
console.log("您的浏览器不支持WebSocket");
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function () {
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function (event) {
setMessageInnerHTML("open");
}
//接收到消息的回调方法
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 closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
启动类:HuawenCloudStockApplication
package com.hwqh.huawencloudstock;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling //该注解是开启定时任务的支持
public class HuawenCloudStockApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(HuawenCloudStockApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(HuawenCloudStockApplication.class, args);
}
}
http://localhost:8080/