JavaEE实现WebSocket(二)使用SpringMvc和AngularJS

本文介绍如何利用SpringMVC和AngularJS构建一个简单的WebSocket聊天室应用,包括服务器端SpringMVC的WebSocket配置及客户端AngularJS的连接处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接上一节的内容。上一节,使用简单JavaEE的注解方法,创建了一个基于Websocket的简易聊天室。在实际开发中,可以直接使用上节的方法。当然,有童鞋会问了,如果我是基于框架开发的,那怎么办?

下面呢,我们一步一步来,构造一个整合了SpringMVC和AngularJS的另一个聊天室吧。

这里,笔者使用的是Spring4.2.3以及AngularJS 1.5.8。

环境的搭建这里不详细描述了,可以在文章的最后下载源码,导入到Eclipse仔细研究。

【服务器端】

这里研究的是SpringMVC框架下的WebSocket服务器的构建方法。从Spring4开始,提供了WebSocket模块的支持。如此,我们可以使用SpringMVC加入WebSocket的支持。

本例使用Spring的注解配置方法,代码如下:


@Configuration
@EnableWebMvc
@EnableWebSocket
@ComponentScan(
        basePackages = "lvhb.test.controller",
        useDefaultFilters = false,
        includeFilters = @ComponentScan.Filter(Controller.class)
)
public class WebServletContextConfiguration extends WebMvcConfigurerAdapter implements WebSocketConfigurer

这里,新加入了一个注解——@EnableWebSocket(没加也没啥大碍),并且实现了WebSocketConfigurer。这里需实现一个注册方法如下:

返回类型

方法和描述

void

registerWebSocketHandlers(WebSocketHandlerRegistry registry)

注册一个WebSocket处理器

本例中,我们注册一个WebSocketHandler,并且加入一个拦截器。(为什么要加拦截器?在WebSocket握手完成前,处理一些必要的准备工作,如:参数的获取,session处理等)

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry)
{
    registry.addHandler(wsHandler(), "/chat").addInterceptors(new HandInterceptor());
}

registerWebSocketHandlers方法中,使用addHandler加入了WebSocket处理器。这里wsHandler()方法实际上是new了一个处理器对象。之后,添加了拦截器,拦截器的代码如下:

/**
 * WebSocket的一个拦截器
 * 此处,在握手前,获取请求中的nickname参数,并将其写入session的Attribute中,方便WsHandler处理session
 * @author lvhb
 *
 */
public class HandInterceptor implements HandshakeInterceptor
{

	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Exception e)
	{}

	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
			Map<String, Object> map) throws Exception
	{
		if (request instanceof ServletServerHttpRequest) {
            HttpServletRequest req = ((ServletServerHttpRequest) request).getServletRequest();
            String userName = req.getParameter("nickname");
            map.put("WEBSOCKET_USERNAME", userName);
            req.getSession().setAttribute("WEBSOCKET_USERNAME", userName);
        }
        return true;
	}

}

拦截器要实现HandshakeInterceptor接口,这里提供了两个方法,一个是在握手前的处理,另一个是在握手后的处理。本例中,我们使用握手前的处理,将request请求转为HttpServletRequest,并从中获取到客户端发送过来的用户昵称。之后,我们将用户名加入session的Attribute内。

之后,我们看重头戏,处理器类的实现,先上代码:

/**
 * WebSocket 处理器
 * @author lvhb
 *
 */
public class WsHandler extends TextWebSocketHandler
{
	private static final Logger logger = LogManager.getLogger("WsHandler");
	/**
	 * 用于记录当前连接的客户端Session
	 */
	private static Map<String, WebSocketSession> clients;
	/**
	 * 用于将JSON映射成对象
	 */
	private static ObjectMapper mapper = new ObjectMapper();
	
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception
	{
		super.handleTextMessage(session, message);
		logger.info("onmessage:"+message.getPayload());
		
		//使用ObjectMapper将获取到的json数据转换成对象
		ChatMsgModel msgModel = WsHandler.mapper.readValue(message.getPayload(), ChatMsgModel.class);
		UserMessage uMsg = new UserMessage(msgModel.getNickname(), msgModel.getText(), LocalDateTime.now());
		broadcastMsg(uMsg);
	}

	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception
	{
		super.afterConnectionEstablished(session);
		if(clients == null)
			clients = new HashMap<String, WebSocketSession>();

		Map<String, Object> attrs = session.getAttributes();
		if(attrs != null)
		{
			String nickname = attrs.get("WEBSOCKET_USERNAME").toString();
			clients.put(nickname, session);
			SystemMessage sysMsg = new SystemMessage((nickname.equals("") ? "有人":nickname) + "进入了聊天室", LocalDateTime.now());
			broadcastMsg(sysMsg);
		}
	}

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception
	{
		super.afterConnectionClosed(session, status);
		if(clients != null && !clients.isEmpty())
		{
			Map<String, Object> attrs = session.getAttributes();
			if(attrs != null)
			{
				String nickname = attrs.get("WEBSOCKET_USERNAME").toString();
				if(clients.containsKey(nickname))
				{
					removeSession(nickname);
					logger.info("ws removed");
					
					SystemMessage sysMsg = new SystemMessage((nickname.equals("") ? "有人":nickname) + "离开了聊天室", LocalDateTime.now());
					broadcastMsg(sysMsg);
				}
			}
		}
	}
	
	private synchronized void removeSession(String nickname)
	{
		clients.remove(nickname);
	}
	
	/**
	 * 广播消息
	 * @param msg
	 */
	private void broadcastMsg(Message msg)
	{
		if(clients == null || clients.isEmpty())
			return;
		
		try{
			for(Map.Entry<String, WebSocketSession> entry:clients.entrySet())
			{
				WebSocketSession session = entry.getValue();
				if(session.isOpen())
					session.sendMessage(msg.parseToTextMessage());
			}
		}catch(IOException ioe)
		{
			logger.error(ioe.getMessage());
		}
	}

	@Override
	protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception
	{
		super.handlePongMessage(session, message);
		logger.info("pongmsg");
	}

	@Override
	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception
	{
		super.handleTransportError(session, exception);
		logger.info("errormsg:"+exception.getMessage());
	}
}

这里WsHandler类继承自TextWebSocketHandler(Spring推荐的方法,用于处理文本类型的数据处理。当然,您也可以实现WebSocketHandler接口)。

我们重写handleTextMessage、afterConnectionEstablished及afterConnectionClosed方法。对应于WebSocket的OnMessage、OnOpen、OnClose。这里具体聊天室业务逻辑处理不做过多介绍。

在每个重写的方法中,都有WebSocketSession对象传入,这个是客户端与服务器端的会话实例。当我们想给客户端发送内容时,可以使用sendMessage方法,将一个实现了WebSocketMessage的对象发送给客户端。当然,这里推荐使用TextMessage的对象。

服务器的内容就介绍到这里。下面简单说一下Angular部分的内容。

【客户端】

Angular部分的内容和上一节的JavaScript操作WebSocket类似。核心代码如下:

/*
* =============== WebSocket处理 ===================
*/
try {
	$scope.server = new WebSocket(WSURL+"?nickname="+$scope.nickname);
} catch(error) {
	console.log("ws connection error");
}
		        
$scope.server.onopen = function(event){
        console.log("ws onopen");
}
$scope.server.onmessage = function(event){
	console.log("ws onmessage");
	var message = event.data;
	appendLine(message);
}
$scope.server.onclose = function(event){
	console.log("ws onclose");
}
$scope.server.onerror = function(event){
	console.log("ws onerror");
}
/*
* ================= WebSocket =====================
*/

这里注意WebSocket在创建的时候如HTTP连接一样,可以带参数的。所以这里传入了nickname表示聊天人的昵称。之后,在处理onmessage时,appendLine方法为将接受到的数据加入到聊天界面中。

		function appendLine(text){
			if(angular.isDefined(text)){
				$scope.$apply($scope.chatData.push(text));
			}
		}

这里注意,笔者使用了$scope.$apply()。让数据修改后立即刷新到页面上。

这个例子实现的截图如下。


好,这一节就到这结束了。大家可以下载源码,运行一下,就会明白SpringMVC的WebSocket如何实现。

点我下载源码

Java SpringMVC WebSocket 是一种在 web 应用中实现实时通信的技术。WebSocket协议是一种基于TCP协议的全双工通信协议,通过 WebSocket 可以在浏览器服务器之间建立持久连接,实现服务器向客户端实时推送数据的功能。 SpringMVC 是一个基于 Java 的开源框架,用于构建 JavaEE 应用程序的 Web 层。它提供了一种轻量级的、快捷的开发方式,可以同时支持传统的请求-响应模式 WebSocket 实时通信模式。 为了在 SpringMVC使用 WebSocket,我们需要使用Spring 的一个模块——Spring Websocket。它提供了一组类接口,用于定义 WebSocket 的端点(Endpoint),配置 WebSocket 的拦截器(Interceptor),处理客户端发送的消息向客户端发送消息等操作。通过这些类接口,我们可以非常方便地实现 WebSocket 的功能。 首先,我们需要定义一个继承自`AbstractWebSocketMessageBrokerConfigurer`的配置类,通过它可以配置 WebSocket 的相关参数,比如设置允许的来源(allowed origins)、配置拦截器等等。 然后,我们需要定义一个继承自`AbstractWebSocketMessageBrokerConfigurer`的类,通过它可以配置消息代理(Message Broker)。消息代理负责接收客户端发送的消息并转发到目标客户端,同时也负责接收服务器推送的消息并发送给目标客户端。 最后,我们可以通过在控制器中添加`@MessageMapping`注解来处理客户端发送的消息,并通过`@SendTo`注解将处理结果发送给客户端。 总之,使用 Java SpringMVC WebSocket 可以很方便地实现实时通信功能,在需要实现实时推送数据的场景下,非常适用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值