接上篇,這篇咱們採用編程式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提示轉換成執行代碼。
爲了你們對兩種模式有個總體的認識,中間的細節咱們都跳過了。但願不會對你們的理解帶來障礙。