需求:定时读取数据,判断后利用websocket 发送消息给特定用户,并利用html5的Notification 发起桌面通知。
pom.xml 中添加相关Maven依赖:
<!-- websockt-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
编写WebSocket相关类。在WebSocketConfig中配置处理器(systemWebSocketHandler)以及拦截器(WebSocketHandshakeInterceptor)
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(systemWebSocketHandler(),"/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor());
webSocketHandlerRegistry.addHandler(systemWebSocketHandler(),"/sockjs/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor())
.setAllowedOrigins("*").withSockJS();
}
@Bean
public WebSocketHandler systemWebSocketHandler(){
return new SystemWebSocketHandler();
}
}
下面是 SystemWebSocketHandler 的配置
public class SystemWebSocketHandler implements WebSocketHandler {
private static final Logger logger;
private static final HashSet<WebSocketSession> users;
static {
users = new HashSet<>();
logger = LoggerFactory.getLogger(SystemWebSocketHandler.class);
}
// @Autowired
// IWebSocketService webSocketService;
@Override
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
users.add(webSocketSession);
}
@Override
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
}
@Override
public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
if (webSocketSession.isOpen()) {
webSocketSession.close();
}
logger.debug("websocket连接关闭......");
users.remove(webSocketSession);
}
@Override
public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
logger.debug("websocket 连接关闭......");
users.remove(webSocketSession);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 向所有用户推送消息
*
* @param message
*/
public void sendMessageToUsers(TextMessage message) {
for (WebSocketSession user : users) {
try {
if (user.isOpen()) {
// synchronized(user) {
user.sendMessage(message);
// }
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 指定用户推送消息
*
* @param roleId
* @param message
*/
public void sendMessageToUser(int roleId, TextMessage message) {
for (WebSocketSession user : users) {
LoginUser loginUser = (LoginUser) user.getAttributes().get("user");
if (loginUser.getRoleId() == roleId || loginUser.getRoleId() == 0) {
try {
if (user.isOpen()) {
synchronized (loginUser) {
user.sendMessage(message);
}
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
/**
* 查看是否存在未处理的举报
*/
public void doTask() {
boolean hasinfo = false;
/**
逻辑处理代码段
**/
if (hasinfo) {
sendMessageToUser(2, new TextMessage("推送内容"));
}
}
}
WebSocketHandshakeInterceptor 的配置(每次建立连接都会进行握手,会被此拦截器拦截)
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
private static Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class);
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
if (session != null) {
//使用userName区分WebSocketHandler,以便定向发送消息
LoginUser user = (LoginUser) session.getAttribute("login");
attributes.put("user",user);
}
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
}
定时进行任务处理
@Scheduled(fixedRate = 1000 * 60 * 60 * 2) // 每隔2小时执行一次
public void scheduleMethod() {
new SystemWebSocketHandler().doTask();
}
定时任务的成功执行,需要在配置文件中启动定时器,使@Schedued 生效
<!-- 启动定时器 -->
<task:annotation-driven/>
进行到这一步后台的功能基本完成,接下来需要在js 中连接webSocket 并接收消息。
var ip = window.location.host;
var websocket;
if ('WebSocket' in window) {
websocket = new WebSocket("ws://" + ip + "/xxx/webSocketServer");
} else if ('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://" + ip + "/xxx/webSocketServer");
} else {
websocket = new SockJS("http://" + ip + "/xxx/sockjs/webSocketServer");
}
websocket.onopen = function (evnt) {
console.info("websocket已连接")
};
websocket.onmessage = function (evnt) {
console.info("后台传来消息")
//发送桌面消息(这里是发送消息相关代码)
};
websocket.onerror = function (evnt) {
console.info("websocket连接出错")
};
websocket.onclose = function (evnt) {
console.info("websocket连接已关闭")
}
为了实现Notification,首先要检查并申请通知权限
window.addEventListener('load', function () {
// 首先,让我们检查我们是否有权限发出通知
// 如果没有,我们就请求获得权限
if (window.Notification && Notification.permission !== "granted") {
Notification.requestPermission(function (status) {
if (Notification.permission !== status) {
Notification.permission = status;
}
});
}
})
以下为实现通知部分的逻辑
// 如果用户同意就创建一个通知
if (window.Notification && Notification.permission === "granted") {
webnotify()
}
// 如果用户没有选择是否显示通知
// 注:因为在 Chrome 中我们无法确定 permission 属性是否有值,因此
// 检查该属性的值是否是 "default" 是不安全的。
else if (window.Notification && Notification.permission !== "denied") {
Notification.requestPermission(function (status) {
if (Notification.permission !== status) {
Notification.permission = status;
}
// 如果用户同意了
if (status === "granted") {
webnotify()
}
// 否则,我们可以让步的使用常规模态的 alert
else {
mynotify()
}
});
}
// 如果用户拒绝接受通知
else {
// 我们可以让步的使用常规模态的 alert
mynotify()
}
mynotify()是自定义的通知实现,此处不贴出实现代码。下面是webnotify()方法的实现。
function webnotify(){
var notify = new Notification(
"通知",
{
dir: 'auto',
lang: 'zh-CN',
icon: '???.png',//通知的缩略图,//icon 支持ico、png、jpg、jpeg格式
body: "" //通知的具体内容
}
);
notify.onclick = function () {
//如果通知消息被点击,通知窗口将被激活
window.focus();
window.open("http://?");//打开指定url
notify.close();
},
notify.onerror = function () {
console.log("HTML5桌面消息出错!!!");
};
notify.onshow = function () {
setTimeout(function () {
notify.close();
}, 20000)
};
notify.onclose = function () {
console.log("HTML5桌面消息关闭!!!");
};
}
至此需求已初步实现,细节处可以慢慢修改。