1. 问题
当在使用WebSocket
服务端向客户端发送消息时使用sendObject
的方式传递信息出现了以下这个错误
javax.websocket.EncodeException: No encoder specified for object of class
2. 原因
这个错误的原因是当我们使用sendObject
方式传递信息时需要指定对应的编码器
对传递信息进行编码,编码器的设置就在@ServerEndpoint注解
上。
原先的@ServerEndpoint
注解内容:
@ServerEndpoint("/api/websocket/client/{clientId}")
只设定了默认的value值
,编码器需要加入encoders
属性,如下:
@ServerEndpoint(value = "/api/websocket/client/{clientId}",encoders = {ServerEncoder.class})
3. 解决方案
在@ServerEndpoint
注解上加入指定编码器类:
@ServerEndpoint(value = "/api/websocket/client/{clientId}",encoders = {ServerEncoder.class})
encoders
的属性是个数组,所以可以指定多个编码器
,编码器的泛型需要指定
,如果发送消息的实体结构没有适合的编码器则会报上面的错误。
3.1 HashMap编码类
这是一个HashMap
的编码类,将HashMap
转为了序列化后的JSON字符串
,核心就是encode方法
,只需要返回Object
序列化后的json字符串就行,这里使用的fastjson
,也可以自定义其他序列化工具来进行序列化操作。
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import java.util.HashMap;
/**
* @author YUSHENGDADA
* @title: ServerEncoder
* @projectName v2_lab
* @description: WebSocket编码器
* @date 2022/8/22 0022上午 11:42
*/
public class HashMapEncoder implements Encoder.Text<HashMap> {
private static final Logger log = LoggerFactory.getLogger(HashMapEncoder.class);
/**
* 这里的参数 hashMap 要和 Encoder.Text<T>保持一致
* @param hashMap
* @return
* @throws EncodeException
*/
@Override
public String encode(HashMap hashMap) throws EncodeException {
/*
* 这里是重点,只需要返回Object序列化后的json字符串就行
* 你也可以使用gosn,fastJson来序列化。
* 这里我使用fastjson
*/
try {
return JSONObject.toJSONString(hashMap);
}catch (Exception e){
log.info("ServerEncoder编码异常:{}",e.getMessage());
}
return null;
}
@Override
public void init(EndpointConfig endpointConfig) {
//可忽略
}
@Override
public void destroy() {
//可忽略
}
}
3.2 实体编码类
在应用场景中直接使用HashMap
传递参数还是不太优雅
和不好维护
,所以这里再提供一个实体编码类
,消息传递实体可以固定,业务实体作为其中data数据的内容
3.2.1 消息实体
@Data
public class BaseInfoModel<T> {
private String code;
private String msg;
private T data;
public static <T> BaseResponseMessage success(T data) {
BaseResponseMessage baseResponseMessage = new BaseResponseMessage();
baseResponseMessage.code = "0";
baseResponseMessage.msg = "成功";
baseResponseMessage.data = data;
return baseResponseMessage;
}
}
3.2.2 实体编码类
import com.an.websocket.model.client.BaseResponseMessage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
/**
* @author YUSHENGDADA
* @title: BaseModelEncoder
* @projectName v2_lab
* @description: 实体编码器
* @date 2022/8/22 0022下午 14:15
*/
public class BaseModelEncoder implements Encoder.Text<BaseResponseMessage> {
@Override
public String encode(BaseResponseMessage baseResponseMessage) throws EncodeException {
try {
JsonMapper jsonMapper = new JsonMapper();
return jsonMapper.writeValueAsString(baseResponseMessage);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
@Override
public void init(EndpointConfig endpointConfig) {
}
@Override
public void destroy() {
}
}
3.2.3 注解配置
实体跟实体编码器配置好后加入到注解的属性上,因为是数组直接HashMap的编码器后加入即可。
代码如下:
@ServerEndpoint(value = "/api/websocket/client/{clientId}",encoders = {HashMapEncoder.class, BaseModelEncoder.class})
发送消息的代码
@OnMessage
public void onMessage(String message, Session session,@PathParam("clientId") String clientId){
UserMessageModel userMessageModel = JSONObject.parseObject(message, UserMessageModel.class);
log.info("客户端:{} 发送到客户端:{},消息内容:{}",clientId,userMessageModel.getAcceptId(),userMessageModel.getMessage());
webSocketClientMap.get(userMessageModel.getAcceptId()).sendMessage(BaseResponseMessage.success(userMessageModel));
}
private void sendMessage(Object message){
try {
this.infoSession.getBasicRemote().sendObject(message);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (EncodeException e) {
throw new RuntimeException(e);
}
}
3.2.4 验证效果
在客户端将收到的消息打印了出来,丑了点,凑合看,结构没问题就行