接上篇,这篇我们采用编程式WebSocket实现上篇的例子:
服务端Endpoint,不再使用ServerEndpoint注解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public class ProgramerServer extends Endpoint {
@Override
public void onOpen(Session session, EndpointConfig edc) {
System.out.println( "Somebody is coming!" );
session.addMessageHandler( new MessageHandler.Whole<String>() {
@Override
public void onMessage(String message) {
System.out.println(message);
try {
session.getBasicRemote().sendText( "it is sickening" );
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
@Override
public void onClose(Session session, CloseReason closeReason) {
// NO-OP by default
}
@Override
public void onError(Session session, Throwable throwable) {
// NO-OP by default
}
} |
而是继承一个Endpoint抽像类,我们发现Endpoint提供的三个方法:onOpen,onClose,onError。
与在声明式WebSocket中存在的四件套:@OnOpen,@OnClose,@OnMessage , @OnError, 相比少了@OnMessage。
那收到消息之后回调什么呢? 从上面的代码可以看到为session增加的MessageHandler有一个相似方法onMessage。对,就是他。接收到消息为调用的就是这个handler的onMessage方法。
难道两种编程方式的运行逻辑还不相同? 其实不然,对于声明式编程,也是通过MessageHandler回调@OnMessage标记的方法。只是这个过程在声明式编程模式中,被Tomcat等作了包装。
(这里透一点,对于声明式编程, Tomcat都会将其转换成本篇的这种模式, 声明式编程中POJO没有继承Endpoint抽像类,Tomcat自已构造一个Endpoint的子类,在Tomcat8中叫PojoEndpointServer。如下继承关系:
1
2
|
public class PojoEndpointServer extends PojoEndpointBase
public abstract class PojoEndpointBase extends Endpoint.
|
后端的运行采用PojoEndpointServer委托给我们的POJO类就可以,同样道理
@ClientEndpoint注解的POJO对应到PojoEndpointClient。)
发现没,没有ServerEndpoint注解, 无法配置端点的映射路径? 这里我们需要声明一个ServerApplicationConfig实体(还记和Restful WS中的那个javax.rs.ws.core.Application吗?)来完成这个功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class MyApplicationConfig implements ServerApplicationConfig{
@Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> allClasses) {
return null ;
}
@Override
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> end) {
ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(ProgramerServer. class , "/chat" )
.configurator( new ServerEndpointConfig.Configurator(){
}).build();
return new HashSet<ServerEndpointConfig>(){{
add(sec);
}};
}
} |
getEndpointConfig构建了一个ServerEndpointConfig集合,上一篇声明式WebSocket为什么不需要这个? 同样需要,只是在声明式WebSocket中Tomcat可以通过@ServerEndpoint注解去构建他。参看Tomcat代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
@Override
public void addEndpoint(Class<?> pojo) throws DeploymentException {
ServerEndpoint annotation = pojo.getAnnotation(ServerEndpoint. class );
// ServerEndpointConfig
ServerEndpointConfig sec;
Class<? extends Configurator> configuratorClazz =
annotation.configurator();
Configurator configurator = null ;
if (!configuratorClazz.equals(Configurator. class )) {
try {
configurator = annotation.configurator().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new DeploymentException(sm.getString(
"serverContainer.configuratorFail" ,
annotation.configurator().getName(),
pojo.getClass().getName()), e);
}
}
sec = ServerEndpointConfig.Builder.create(pojo, path).
decoders(Arrays.asList(annotation.decoders())).
encoders(Arrays.asList(annotation.encoders())).
subprotocols(Arrays.asList(annotation.subprotocols())).
configurator(configurator).
build();
addEndpoint(sec);
}
|
Tomcat为每一个ServerEndpoint构造了一个ServerEndpointConfig。
将上面两个类同时,打入War包,部署到Tomcat上,一个WebSocket服务端就OK了。
现在你可以用上篇的Client去访问这个WebSocket。或者你已厌倦了Annocation. 来一个编程式Client吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class ProgramerClient extends Endpoint {
@Override
public void onOpen(Session session, EndpointConfig edc) {
System.out.println( "I was accpeted by her!" );
session.addMessageHandler( new MessageHandler.Whole<String>() {
@Override
public void onMessage(String message) {
System.out.println( "she say: " + message);
}
});
}
} |
为什么没有onClose,onError方法? 因为Endpoint中有默认实现,这里就没有重载。
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Client {
public static void main(String[] args) throws DeploymentException, IOException, InterruptedException {
WebSocketContainer ws = ContainerProvider.getWebSocketContainer();
String url = "ws://localhost:8080/ChatWeb2/chat" ;
ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator( new MyClientConfigurator()).build();
Session session = ws.connectToServer(ProgramerClient. class ,cec,URI.create(url));
session.getBasicRemote().sendText( "Hello,chick!" );
Thread.currentThread().sleep( 10000 );
}
} |
等等,有点不同。当然了,这里没有了ClientEndpoint,当然也就没有了@ClientEndpoint.Configurator字段(还记得@ClientEndpoint的结构吗?)
当然也就没有了ClientEndpointConfig。所以需要我们自已加一个。
可以看出编程式WebSocket端点比Annotation复杂了很多。采用Annotation提示使用编程变得简单,
而对于WebSocket容器(即本文的Tomcat等)则需要将这种Annotation提示转换成执行代码。
为了大家对两种模式有个整体的认识,中间的细节我们都跳过了。希望不会对大家的理解带来障碍。
本文转自 anranran 51CTO博客,原文链接:http://blog.51cto.com/guojuanjun/1963506