请查看我下面发布的代码。仅供参考,这是来自Oracle网站的websocket样本:https://netbeans.org/kb/docs/javaee/maven-websocketapi.html
我的问题是,这是如何工作的?——特别是MyWhiteboard
的broadcastFigure
函数。它不是一个被重写的抽象函数,也不像传统意义上那样"注册"到另一个类。我看到它的唯一方法是当编译器看到@OnMessage
注释时,它会在收到新消息时将broadcastFigure
调用插入编译的代码中。但是在调用这个函数之前,它通过FigureDecoder
类来处理接收到的数据——基于在注释@ServerEndpoint
中指定的这个解码器。在broadcastFigure
中,当调用sendObject
时,编译器根据注释@ServerEndpoint
中指定的内容插入对FigureEncoder
的引用。这准确吗?
如果是这样的话,为什么这个实现使用注释这样做呢?在查看此之前,我希望有一个抽象的OnMessage
函数需要被覆盖,并为编码器和解码器显式注册函数。为什么websocket不是通过这种"传统"的方式来实现,而是通过注释来实现呢?
谢谢。
Mywhiteboard.java:
@ServerEndpoint(value = "/whiteboardendpoint", encoders = {FigureEncoder.class}, decoders = {FigureDecoder.class})
public class MyWhiteboard {
private static Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>());
@OnMessage
public void broadcastFigure(Figure figure, Session session) throws IOException, EncodeException {
System.out.println("broadcastFigure: " + figure);
for (Session peer : peers) {
if (!peer.equals(session)) {
peer.getBasicRemote().sendObject(figure);
}
}
}
@OnError
public void onError(Throwable t) {
}
@OnClose
public void onClose(Session peer) {
peers.remove(peer);
}
@OnOpen
public void onOpen(Session peer) {
peers.add(peer);
}
}
FigureEncoder.java
public class FigureEncoder implements Encoder.Text<Figure> {
@Override
public String encode(Figure figure) throws EncodeException {
return figure.getJson().toString();
}
@Override
public void init(EndpointConfig config) {
System.out.println("init");
}
@Override
public void destroy() {
System.out.println("destroy");
}
}
FigureDecoder.java:
public class FigureDecoder implements Decoder.Text<Figure> {
@Override
public Figure decode(String string) throws DecodeException {
JsonObject jsonObject = Json.createReader(new StringReader(string)).readObject();
return new Figure(jsonObject);
}
@Override
public boolean willDecode(String string) {
try {
Json.createReader(new StringReader(string)).readObject();
return true;
} catch (JsonException ex) {
ex.printStackTrace();
return false;
}
}
@Override
public void init(EndpointConfig config) {
System.out.println("init");
}
@Override
public void destroy() {
System.out.println("destroy");
}
}
注释有其优点和缺点,选择创建基于注释的API与使用(怎么说)使用接口的"传统"API。我就不一一细说了,因为你会发现网上有很多战争。
如果使用得当,注释可以更好地说明类/方法的职责是什么。许多人更喜欢注释,因此它们已经成为一种趋势,并且无处不在。说完了这些,让我们回到你的问题:
为什么这个实现使用注释这样做?在看这个之前,我希望有一个抽象的OnMessage函数需要被覆盖,并为编码器和解码器显式注册函数。为什么websocket不是通过这种"传统"的方式来实现,而是通过注释来实现呢?
事实上他们没有。注释只是使用API的一种提供方式。如果你不喜欢,你可以按老方法做。以下是来自JSR-356规范:
创建端点有两种主要方法。第一种手段是实现一定的来自Java WebSocket API的API类,具有处理端点生命周期所需的行为;使用和发送消息、发布自身或连接到对等体。通常,此规范将引用此作为一个程序化的终点。第二种方法是修饰一个普通的旧Java对象(POJO)带有来自Java WebSocket API的某些注释。然后实现获取这些并在运行时创建适当的对象,以将POJO部署为websocket端点。通常,该规范将这种端点称为带注释的端点。
再说一遍,人们更喜欢使用注释,这也是你会发现大多数教程使用的,但如果你非常想要它,你可以不使用它们。