springboot整合websocket实现页面在线客服聊天,并且解决无法注入实例的问题

本文采用现在流行的springboot框架整合websocket实现在线客服的聊天功能,代码实现如下:

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.zqj</groupId>
	<artifactId>webchat</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>webchat</name>
	
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.2.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>

	<dependencies>
		<!-- springboot web -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-tomcat</artifactId>
					<scope>compile</scope>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		
		<!--spring websocket -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>
		
		<!-- lombok pojo log -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.0</version>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

springboot入口类:

package com.zqj.chat;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@SpringBootApplication
public class WebChatApplication 
{
    public static void main( String[] args )
    {
        SpringApplication.run(WebChatApplication.class, args);
    }
    
    /**
     * 
    * @Title: serverEndpointExporter  
    * @Description: 检测服务类实现
    * @return ServerEndpointExporter 
    * @throws
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter (){
        return new ServerEndpointExporter();
    }
}

websocket服务实现WebSocketService

package com.zqj.chat.service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;

@ServerEndpoint("/websocket/{userId}")
@Component
@Slf4j
public class WebSocketService {
	
	public static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
	
	@OnOpen
	public void onOpen(@PathParam("userId") String userId,Session session) {
		log.info("WebSocketService onOpen: "+userId);
		if(sessionMap == null) {
			sessionMap = new ConcurrentHashMap<String, Session>();
		}
		sessionMap.put(userId, session);
	}
	
	@OnClose
	public void OnClose(@PathParam("userId") String userId) {
		log.info("WebSocketService OnClose: "+userId);
		sessionMap.remove(userId);
	}
	@OnMessage
	public void OnMessage(@PathParam("userId") String userId,Session session,String message) {
		log.info("WebSocketService OnMessage: "+message);
		for(Session session_ : sessionMap.values()) {
			session_.getAsyncRemote().sendText(message);
		}
	}
	
	@OnError
	public void error(Session session, Throwable t) {
		
		t.printStackTrace();
	}
}

测试页面:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
    <title>聊天室</title>
    <link rel="stylesheet" type="text/css" href="css/easyui.css">
		
		<script type="text/javascript" src="js/jquery.min.js"></script>
		<script type="text/javascript" src="js/jquery.easyui.min.js"></script>
		<script type="text/javascript" src="js/json2.js"></script>
		<style type="text/css">
*{font-size: 14px; padding:0; margin:0;}
#chatRecord{
 position: relative;
 margin: 20px auto;
 border: 1px solid steelblue;
 width: 600px;
 height: 500px;
}

.msgContent{
 width:auto;
 max-width: 250px;
 height: auto;
 word-break: break-all;
 margin: 5px;
 padding: 3px;
 border-radius: 5px;
}
 
#chatZone .left{
list-style:none;
 text-align: left;
}
#chatZone .left .p{
 float: left;
 text-align: left;
 background-color: lightgrey;
 font-size: 4ex;
 
}
#chatZone .left .title{
 text-align: left;
 font-size: 5px;
 color:grey;
}
#chatZone .right{
float: right;
 list-style:none;
 text-align: right;
}
#chatZone .right .title{
 text-align: right;
 font-size: 5px;
 color:grey;
 clear:both;
}
 #chatZone .right .p{
 float: right;
 text-align: right;
 background-color: yellowgreen;
 font-size: 4ex;
 
}

 
</style>
</head>
<body>
     <p>
    帐号<input type="text" id="linkAgent" class="easyui-textbox" style="width: 150px" /> 
    昵称<input type="text" id="nickname" class="easyui-textbox" style="width: 150px" /> 
    <a id="btnLink" href="javascript:void(0)" class="easyui-linkbutton c1">开始聊天</a></p>
    <div data-options="region:'center'" class="easyui-panel" style="width: 50%;height:500px;  padding: 5px;" id="chatRecord">
						<ul class="chatDialog-main clearfix" id="chatZone">
				
						
				
						</ul>
					</div>
		<div class="easyui-panel" style="width: 50%; padding: 5px;">
							<table height="100%" cellpadding="0px" cellspacing="0px">
								<tr>
									<td height="100px">
										<table cellpadding="5px" cellspacing="0" border="0">
											<tr style="vertical-align: middle;">
												<td>
													<textarea
														style="width: 600px; height: 100px; overflow: auto; vertical-align: middle;"
														id="txtMessage" name="txtMessage"></textarea>
												</td>
												<td style="width: 6px"></td>
												<td style="vertical-align: middle;">
													<a id="btnSend" href="javascript:void(0)"
														class="easyui-linkbutton c3" data-options="size:'large'">发送</a>
												</td>
											</tr>
										</table>
									</td>
								</tr>
							</table>
						</div>						
    
</body>
<script type="text/javascript">
function getNowFormatDate() {
    var date = new Date();
    var seperator1 = "-";
    var seperator2 = ":";
    var month = date.getMonth() + 1;
    var strDate = date.getDate();
   	var hours = date.getHours();
   	var minutes = date.getMinutes();
   	var seconds = date.getSeconds();
    if (month >= 1 && month <= 9) {
        month = "0" + month;
    }
    if (strDate >= 0 && strDate <= 9) {
        strDate = "0" + strDate;
    }
    if (hours >= 0 && hours <= 9) {
    	hours = "0" + hours;
    }
    if (minutes >= 0 && minutes <= 9) {
    	minutes = "0" + minutes;
    }
    if (seconds >= 0 && seconds <= 9) {
    	seconds = "0" + seconds;
    }
    var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate
            + " " + hours + seperator2 + minutes
            + seperator2 + seconds;
    return currentdate;
}
$(function() {
	var websocket;
	var nickname;
	$(window).keyup(function(event){
		if(event.keyCode == 13) {
			$("#btnSend").click();
			return true;
        } 
	});
	$("#btnSend").click(function() {
		var from = $("#linkAgent").val();
		var msg = $("#txtMessage").val();
		var obj=new Object();
		obj.from=from;
		obj.message=msg;
		
		obj.nickname=nickname;
		var json = JSON.stringify(obj);
		var result =websocket.send(json);
		var message ="<li class='msgContent right'>"+
						"<p class='msgContent right title'>"+nickname+" ("+getNowFormatDate()+"):</p>"+
		   				"<p class='msgContent right p'>"+msg+"</p></li>"+
		   				"<div style='clear:both'></div>";
		$("#chatZone").append(message);
		$("#txtMessage").val("");
		
	});
	
	$("#btnLink").click(function() {
		var agent = $("#linkAgent").val();
		nickname = $("#nickname").val();
		if('WebSocket' in window){
			
		}else{
			alert("您的浏览器版本太低,请升级浏览器版本!");
			return;
		}
		if('WebSocket' in window){
			var wsUrl = "ws://"+window.location.host+"/websocket/";
			
			websocket = new WebSocket(wsUrl+agent);
		
		    
		     
		   //连接发生错误的回调方法
		     websocket.onerror = function(){
		         console.log(" websocket.onerror :error");
		     };

		     //连接成功建立的回调方法
		     websocket.onopen = function(event){
		     	
		     }

		     //接收到消息的回调方法
		     websocket.onmessage = function(event){
		    	 msg = eval('(' + event.data + ')');
		    	 if(msg.from != agent) {
		    		 var message ="<li class='msgContent left'>"+
						"<p class='msgContent left title'>"+msg.nickname+" ("+getNowFormatDate()+"):</p>"+
		   				"<p class='msgContent left p'>"+msg.message+"</p></li>"+
		   				"<div style='clear:both'></div>";
						$("#chatZone").append(message);
		    	 }
		     	
		     }

		     //连接关闭的回调方法
		     websocket.onclose = function(){
		    	console.log(" websocket.onclose :close");
		   		websocket = new WebSocket(wsUrl+agent);
		     }

		     //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
		     window.onbeforeunload = function(){
		         websocket.close();
		     }
		}
	});
	
});
</script>
</html>

打开浏览器访问:http://127.0.0.1:8080/webchat.html,界面如下:




在开发过程中遇到了使用注解@ServerEndpoint时,会发生无法自动注入其他实例的情况,解决方案如下:

先写一个config类:

package com.libang.manage.websocket;

import javax.websocket.server.ServerEndpointConfig.Configurator;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyEndpointConfigure extends Configurator implements
		ApplicationContextAware {

	private static volatile BeanFactory context;

	@Override
	public <T> T getEndpointInstance(Class<T> clazz)
			throws InstantiationException {
		return context.getBean(clazz);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		System.out.println("auto load" + this.hashCode());
		MyEndpointConfigure.context = applicationContext;
	}

}

将WebsocketService类上的@ServerEndpoint(value="/websocket/{userId}")改成

@ServerEndpoint(value="/websocket/{userId}",configurator=MyEndpointConfigure.class) 

就可以完美的使用@Autowired注入实例。


展开阅读全文

没有更多推荐了,返回首页