本文主要来讲解如何使用WebSocket来实现一个简易的群聊功能。
引入maven依赖
代码语言:javascript复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
项目的目录结构
接下来,我们来分析每个类的作用。
WebSocketApplication
代码语言:javascript复制@SpringBootApplication
public class WebSocketApplication {
public static void main(String[] args) {
SpringApplication.run(WebSocketApplication.class, args);
}
}
这个类就是一个简单的启动引导类的功能。
WebSocketServer:群聊核心类
代码语言:javascript复制@Component
@ServerEndpoint(value = "/chat/{username}", configurator = SpringBasedConfigurator.class)
public class WebSocketServer {
private final Map<Session, String> userSessionMap = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
userSessionMap.put(session, username);
multicastMessage(session, "欢迎" username "加入群聊!");
}
@OnMessage
public void onMessage(Session session, String message) {
String username = userSessionMap.get(session);
multicastMessage(session, username ": " message);
}
@OnClose
public void onClose(Session session) {
String username = userSessionMap.remove(session);
multicastMessage(session, username "退出群聊。");
}
private void multicastMessage(Session session, String message) {
for (Session userSession : userSessionMap.keySet()) {
if (userSession == session) {
//是自己,忽略
continue;
}
try {
userSession.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@ServerEndpoint:作用是用来表明当前类是一个节点类,当连接成功之后,用户的操作都会回调这个类对应的对象的方法,对象怎么创建的是根据configurator 属性对应的类来实现的,默认是每个连接对应的对象都是通过反射构建的,但是对于本群聊功能来说,是通过SpringBasedConfigurator对象来获取,这个类的作用接下来会剖析。
@OnOpen:当客户端与服务端建立连接的时候,会回调 @OnOpen 注解标记的方法
@PathParam:可以看成跟spring mvc中的@PathVariable注解作用一样,就是取出连接路径中的占位符对应的值
@OnMessage:当客户端发送消息给服务端的时候,会回调OnMessage 注解标记的方法
@OnClose:当客户端断开连接的时候,会回调@OnClose注解标记的方法。
Session:是代表当前客户端与服务端建立的一个会话,通过这个对象,服务端可以主动给客户端发送消息。
WebSocketServer这个类的主要作用是保存每个客户端与服务端建立的连接,一旦有客户端跟服务端建立连接、发送消息、断开连接,都会发送消息给其他客户端,从而实现群聊的功能。
SpringBasedConfigurator
代码语言:javascript复制@Component
public class SpringBasedConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringBasedConfigurator.applicationContext = applicationContext;
}
@Override
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
return applicationContext.getBean(clazz);
}
}
这个类是继承了WebSocket包提供的api ServerEndpointConfig.Configurator ,重写了 getEndpointInstance 方法,主要是因为WebSocket是通过调用getEndpointInstance方法来获取每个连接对应调用的对象, 而getEndpointInstance方法默认是通过直接通过反射构造的,而不是从spring容器获取连接对象,导致类中的像@Resource 这类注解无法生效,所以重写了getEndpointInstance方法,让每个连接对应调用的对象都是从spring容器中获取
WebSocketConfiguration:配置类
代码语言:javascript复制@Configuration
public class WebSocketConfiguration {
/**
* 这个类的主要注册每个加了{@link javax.websocket.server.ServerEndpoint}的 spring bean节点,这算是spring整合websocket的一个体现
* 具体是怎么实现注册的,可以看看 {@link ServerEndpointExporter#afterSingletonsInstantiated()}方法的实现
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
暴露每个加@ServerEndpoint注解的spring bean,算是spring跟WebSocket整合的一处体现。
测试
好了,说完这些类的功能,那么开启测试吧。WebSocket客户端,我们使用https://www.idcd.com/tool/socket网站来模拟。
通过启动引导类来启动项目之后,我们输入 ws://localhost:8080/chat/sanyou, 建立一个连接,模拟一个客户端。
输入连接地址,点击连接,就会显示连接成功,username这里我们填sanyou,填什么都无所谓,相当于一个名字。
我们再建立一个连接,模拟另一个客户端。
第一个客户端就会显示这条信息。
接下来,就可以在发送栏往服务端发送消息,服务端会转给其他的客户端,实现群聊的功能,效果如下。
如果还想加入群聊,另外建立连接就行了,这样,一个简易的群聊功能就完成了。
以上就是本篇文章的全部内容,代码我已经上传到https://github.com/sanyou3/sanyou-parent.git 仓库上了,有更详细的注释,我们下篇文章再见。