接上篇,这篇咱们采用编程式WebSocket实现上篇的例子:java
服务端Endpoint,再也不使用ServerEndpoint注解:编程
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
那收到消息以后回调什么呢? 从上面的代码能够看到为session增长的MessageHandler有一个类似方法onMessage。对,就是他。接收到消息为调用的就是这个handler的onMessage方法。ide
难道两种编程方式的运行逻辑还不相同? 其实否则,对于声明式编程,也是经过MessageHandler回调@OnMessage标记的方法。只是这个过程在声明式编程模式中,被Tomcat等做了包装。ui
(这里透一点,对于声明式编程, Tomcat都会将其转换成本篇的这种模式, 声明式编程中POJO没有继承Endpoint抽像类,Tomcat自已构造一个Endpoint的子类,在Tomcat8中叫PojoEndpointServer。以下继承关系:url
public class PojoEndpointServer extends PojoEndpointBase public abstract class PojoEndpointBase extends Endpoint.
后端的运行采用PojoEndpointServer委托给咱们的POJO类就能够,一样道理code
@ClientEndpoint注解的POJO对应到PojoEndpointClient。)server
发现没,没有ServerEndpoint注解, 没法配置端点的映射路径? 这里咱们须要声明一个ServerApplicationConfig实体(还记和Restful WS中的那个javax.rs.ws.core.Application吗?)来完成这个功能:blog
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代码:
@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吧:
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中有默认实现,这里就没有重载。
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提示转换成执行代码。
为了你们对两种模式有个总体的认识,中间的细节咱们都跳过了。但愿不会对你们的理解带来障碍。