版本历史&代码示例之:WebSocket、JSTL

开发 后端
WebSocket作为长连接的轻量级解决方案,会是B/S的新宠,一举替掉之前的长轮训等方案。滚滚长江东逝水,这或许就印证着技术在进步,时代在发展。

前言

你好,我是方同学(YourBatman)

若你还不太清楚Java EE是什么,可先移步这里:什么是Java EE?

紧接着上一篇讲完Servlet、JSP、EL表达式后,本文尝试把WebSocket和JSTL再疏通疏通。

[[424135]]

所属专栏

BATutopia-Java EE

相关下载

  • 工程源代码:https://github.com/yourbatman/BATutopia-java-ee
  • 【女娲Knife-Initializr工程】访问地址:http://152.136.106.14:8761
  • Java开发软件包(Mac):https://wangpan.yourbatman.cn/s/rEH0 提取码:javakit
  • 程序员专用网盘上线啦,开放注册送1G超小容量,帮你实践做减法:https://wangpan.yourbatman.cn

版本约定

Java EE:6、7、8

Jakarta EE:8、9、9.1

正文

WebSocket

WebSocket是一种在单个TCP连接上进行全双工通信的协议。随着HTML5的诞生,WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

WebSocket协议本质上是一个基于TCP的协议,它由通信协议和编程API组成,WebSocket能够在浏览器和服务器之间建立双向连接,以基于事件的方式,赋予浏览器实时通信能力。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。工作流程如下图:

Java API for WebSocket是Java的Web套接字,在2013年6月份伴随着Java EE 7推出(1.0版本),Java EE 8升级到1.1版本。

注意:WebSocket的Client可以是浏览器,也可是WebSocket的终端(如Java应用、Go应用)。

  1. <!-- javax命名空间版本(Tomcat 9.x及以下版本支持) --> 
  2. <dependency> 
  3.     <groupId>javax.websocket</groupId> 
  4.     <artifactId>javax.websocket-api</artifactId> 
  5.     <version>1.1</version> 
  6.     <scope>provided</scope> 
  7. </dependency> 
  8.  
  9. <!-- jakarta命名空间版本(Tomcat 10.x及以上版本支持) --> 
  10. <dependency> 
  11.     <groupId>jakarta.websocket</groupId> 
  12.     <artifactId>jakarta.websocket-api</artifactId> 
  13.     <version>2.0.0</version> 
  14.     <!-- <version>1.1.2</version> 此版本命名空间同javax --> 
  15.     <scope>provided</scope> 
  16. </dependency> 
  17.  
  18. 除此之外,一般情况下我们直接使用Web容器提供的Jar即可,如Tomcat 
  19. <dependency> 
  20.     <groupId>org.apache.tomcat.embed</groupId> 
  21.     <artifactId>tomcat-embed-websocket</artifactId> 
  22.     <version>Tomcat版本号</version> 
  23. </dependency> 
  24. <dependency> 
  25.     <groupId>org.apache.tomcat</groupId> 
  26.     <artifactId>tomcat-websocket</artifactId> 
  27.     <version>Tomcat版本号</version> 
  28. </dependency> 

版本历程

servlet-3.1版本开始支持。WebSocket 1.1 版与 1.0 版完全向后兼容,只在javax.websocket.Session中添加了两个方法:

  1. <T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> handler) throws IllegalStateException; 
  2. <T> void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> handler) throws IllegalStateException; 

生存现状

作为Http协议的“补充”,很好的弥补了其不足,在Web领域实时推送,也被称作Realtime技术。这种技术大大提升交互体验,拥有广泛的应用场景:在线聊天(如web版微信)、在线客服系统、评论系统等等。

总的来讲,WebSocket作为新贵,生存现状挺好,前景一片光明。

实现(框架)

WebSocket其实是构建在Http协议之上的,所以对于Java语言来讲它依旧由Web容器来提供实现。

概念区分:Web容器不一定是Servlet容器,而Servlet容器一定是Web容器

除此之外也有独立实现:

  • client端实现:org.eclipse.jetty.websocket:javax-websocket-client-impl
  • server端实现:org.eclipse.jetty.websocket:javax-websocket-server-impl对于Client来讲,一般都是浏览器。

代码示例

前面有提到,WebSocket的Client端既可以是浏览器(现代的浏览器100%都支持此协议,若需要考虑浏览器兼容问题(比如国外现在依旧有使用老版IE浏览器的),可以使用socketio框架哈),也可以是Java应用。本示例就加点“难度”,用Java应用作为WebSocket的客户端。当然喽,服务端肯定也是Java应用呀。

创建demo项目,结构如下:

其中client为jar,server为war。

书写Server端代码,提供一个服务端点:

  1. /** 
  2.  * 在此处添加备注信息 
  3.  * 
  4.  * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a> 
  5.  * @site https://yourbatman.cn 
  6.  * @date 2021/9/12 15:29 
  7.  * @since 0.0.1 
  8.  */ 
  9. @ServerEndpoint("/websocket/chat"
  10. public class WsServer { 
  11.  
  12.     // 当前连接上来的连接们(每一个连接都是一个WsServerDemo实例,包含一个Session会话) 
  13.     private static Set<WsServer> webSocketSet = new CopyOnWriteArraySet<>(); 
  14.     // 会话 
  15.     private Session session; 
  16.  
  17.     /** 
  18.      * 建连成功的回调 
  19.      */ 
  20.     @OnOpen 
  21.     public void onOpen(Session session) { 
  22.         this.session = session; 
  23.         webSocketSet.add(this); // 保存当前连接 
  24.         System.out.println("Server有新连接加入!当前在线人数为" + webSocketSet.size()); 
  25.     } 
  26.  
  27.     /** 
  28.      * 连接关闭调用的方法 
  29.      */ 
  30.     @OnClose 
  31.     public void onClose() { 
  32.         webSocketSet.remove(this); 
  33.         System.out.println("Server有一连接关闭!当前在线人数为" + webSocketSet.size()); 
  34.     } 
  35.  
  36.     /** 
  37.      * 收到客户端消息后调用的方法 
  38.      */ 
  39.     @OnMessage 
  40.     public void onMessage(String message) throws IOException { 
  41.         System.out.println("Server来自客户端的消息:" + message); 
  42.         sendMessage("会话[" + session.getId() + "]的消息已经收到,内容为:" + message); 
  43.         // // =======群发消息========= 
  44.         // for (WsServerDemo item : webSocketSet) { 
  45.         //     try { 
  46.         //         item.sendMessage(message); 
  47.         //     } catch (IOException e) { 
  48.         //         continue
  49.         //     } 
  50.         // } 
  51.     } 
  52.  
  53.     /** 
  54.      * 发生错误时调用 
  55.      */ 
  56.     @OnError 
  57.     public void onError(Throwable error) { 
  58.         System.out.println("Server发生错误:" + error.getMessage()); 
  59.     } 
  60.  
  61.     /** 
  62.      * 发送消息 
  63.      */ 
  64.     public void sendMessage(String message) throws IOException { 
  65.         this.session.getBasicRemote().sendText(message); 
  66.     } 
  67.  

我这里简便起见,使用web容器直接实现。有兴趣/想深究websocket的同学,可使用org.eclipse.jetty.websocket:javax-websocket-server-impl通过API方式去启动Server,本文只演示用该方式启动client哈,二者兼顾嘛。

使用编程方式书写client端代码:

  1. /** 
  2.  * 在此处添加备注信息 
  3.  * 
  4.  * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a> 
  5.  * @site https://yourbatman.cn 
  6.  * @date 2021/9/12 15:31 
  7.  * @since 0.0.1 
  8.  */ 
  9. @ClientEndpoint 
  10. public class WsClient { 
  11.  
  12.     // 会话(与服务端建立的会话) 
  13.     private Session session; 
  14.  
  15.     /** 
  16.      * 建连成功的回调 
  17.      */ 
  18.     @OnOpen 
  19.     public void onOpen(Session session) throws IOException { 
  20.         this.session = session; 
  21.         System.out.println("Client连接到服务端成功,会话ID:" + session.getId()); 
  22.         sendMessage("这是一条来自Client端,会话["+session.getId()+"]的消息"); 
  23.     } 
  24.  
  25.     @OnMessage 
  26.     public void onMessage(String message) { 
  27.         System.out.println("Client端收到消息: " + message); 
  28.     } 
  29.  
  30.     @OnClose 
  31.     public void onClose() { 
  32.         System.out.println("Client会话" + session.getId() + "已断开"); 
  33.     } 
  34.  
  35.     /** 
  36.      * 发送消息 
  37.      */ 
  38.     public void sendMessage(String message) throws IOException { 
  39.         this.session.getBasicRemote().sendText(message); 
  40.     } 

用main方法启动Client端,去连接Server端并发送消息:

  1. public class ClientApp { 
  2.  
  3.     private static URI uri = URI.create("ws://localhost:8080/websocket/chat"); 
  4.     private static Session session; 
  5.  
  6.     public static void main(String[] args) throws DeploymentException, IOException, InterruptedException { 
  7.         WebSocketContainer container = ContainerProvider.getWebSocketContainer(); 
  8.  
  9.         // 顺序执行5次会话,端口后再建立新会话 
  10.         for (int i = 0; i < 5; i++) { 
  11.             session = container.connectToServer(WsClient.class, uri); 
  12.             session.close(); 
  13.             TimeUnit.SECONDS.sleep(2); 
  14.         } 
  15.     } 

client端控制台日志:

  1. Client连接到服务端成功,会话ID:1 
  2. Client端收到消息: 会话[0]的消息已经收到,内容为:这是一条来自Client端,会话[1]的消息 
  3. Client会话1已断开 
  4. Client连接到服务端成功,会话ID:2 
  5. Client端收到消息: 会话[1]的消息已经收到,内容为:这是一条来自Client端,会话[2]的消息 
  6. Client会话2已断开 
  7. Client连接到服务端成功,会话ID:3 
  8. Client端收到消息: 会话[2]的消息已经收到,内容为:这是一条来自Client端,会话[3]的消息 
  9. Client会话3已... 

server端控制台日志:

  1. Server有新连接加入!当前在线人数为1 
  2. Server来自客户端的消息:这是一条来自Client端,会话[1]的消息 
  3. Server有一连接关闭!当前在线人数为0 
  4. Server有新连接加入!当前在线人数为1 
  5. Server来自客户端的消息:这是一条来自Client端,会话[2]的消息 
  6. Server有一连接关闭!当前在线人数为0 
  7. Server有新连接加入!当前在线人数为1 
  8. Server来自客户端的消息:这是一条来自Client端,会话[3]的消息 
  9. Server有一连接关闭!当前在线人数为0 

说明:本文特意使用Java应用作为Client端是想让你更深刻的理解WebSocket的用法,实际场景中,其实大都是B/S模式,通过JavaScript作为客户端建立连接(相对简单)。

工程源代码:https://github.com/yourbatman/BATutopia-java-ee

JSTL

Java server pages standarded tag library,即JSP标准标签库。主要提供给Java Web开发人员一个标准通用的标签库,开发人员可以利用这些标签取代 JSP页面上的Java代码,从而提高程序的可读性,降低程序的维护难度。

JSTL强依赖于JSP的存在而存在。

JSTL和EL表达式的目的是一样的:取代JSP页面上写Java代码。它比EL更为强大些,可以完成一些结构化逻辑任务,如:迭代、条件判断、XML文档操作、国际化、SQL等,下面简要介绍其主要标签。

  • 核心标签:也是著名C标签。在JSP文件开头引入c标签<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>,支持的常用函数有:

  1. 1. <c:out>:用于在页面输出 
  2.  - <c:out value="expression" default="expression" escapeXml="boolean"/> 
  3. 2. <c:if>:逻辑判断 
  4.  - <c:if test="expression" var="name" scope="scope">   
  5.        body content   
  6.    </c:if> 
  7. 3. <c:choose>:逻辑判断。when和otherwise的父标签 
  8. 4. <c:when>: <c:otherwise>: 
  9.  - <c:choose>   
  10.      <c:when test="expression">   
  11.          body content   
  12.      </c:when>   
  13.   ... 
  14.      <c:otherwise>   
  15.       body content   
  16.   </c:otherwise>   
  17.    </c:choose> 
  18.   
  19. 5. <c:foreach>: 
  20.  - <c:forEach var="name" items="expression" varStatus="name" begin="expression" end="expression" step="expression">   
  21.        body content   
  22.    </c:forEach> 
  23.  
  24. 6. <c:url>:使用可选的查询参数创造一个URL地址 
  25. 7. <c:set>:设置数据 
  26. 8. <c:remove>:删除数据 
  • 格式化标签:可对数字、日期时间等格式化。<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>,主要函数:

  1. 1. <fmt:formatNumber>:格式化数字 
  2. 2. <fmt:parseNumber>:解析字符串到数字、货币、百分比 
  3. 3. <fmt:formatDate>: 
  4. 4. <fmt:parseData>: 
  • JSTL函数:一般用于辅助标签控制行为。<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>,主要函数:

  1. 1. fn:contains:判断字符串是否包含另外一个字符串  
  2.  - <c:if test="${fn:contains(name, searchString)}"
  3. 2. fn:indexOf:子字符串在母字符串中出现的位置  
  4.  - ${fn:indexOf(name'-')} 
  5. 3. fn:toLowerCase:转为小写 
  6.  - ${fn.toLowerCase(product.name)} 
  • SQL标签,<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>。主要函数:
  1. 1. <sql:setDataSource>、<sql:query>、<sql:update

  • XML标签,<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>。主要函数:
  1. 1. <x:parse>、<x:if>、<x:forEach> 

除此之外,还提供了扩展点:自定义标签。

  1. <!-- javax命名空间版本(Tomcat 9.x及以下版本支持) --> 
  2. <dependency> 
  3.     <groupId>javax.servlet.jsp.jstl</groupId> 
  4.     <artifactId>jstl-api</artifactId> 
  5.     <version>1.2</version> 
  6. </dependency> 
  7.  
  8. <!-- jakarta命名空间版本(Tomcat 10.x及以上版本支持) --> 
  9. <dependency> 
  10.     <groupId>jakarta.servlet.jsp.jstl</groupId> 
  11.     <artifactId>jakarta.servlet.jsp.jstl-api</artifactId> 
  12.     <version>2.0.0</version> 
  13.     <!-- <version>1.2.7</version> 此版本命名空间同javax --> 
  14. </dependency> 

说明:之前可能需要有jstl.jar和standard.jar两个Jar,但从1.2版本后这一个GAV即可。当然喽,99.99%情况下该jar无需你导入,由web容器负责

版本历程

JSTL也依赖于JSP而存在,所以和JSP版本有强关系。

JSTL 1.2版本可断定是最后一个版本,因为JSP已走到尽头,所以它也会随之消亡。

生存现状

同JSP。

实现(框架)

与Servlet相同的Web容器,由Web容器提供解析能力。如tomcat的标签库实现:http://tomcat.apache.org/taglibs

代码示例

实在没有应用场景了,略。

工程源代码:https://github.com/yourbatman/BATutopia-java-ee

总结

WebSocket作为长连接的轻量级解决方案,会是B/S的新宠,一举替掉之前的长轮训等方案。滚滚长江东逝水,这或许就印证着技术在进步,时代在发展。

作为老一辈程序员的我,对EL表达式、JSTL这类技术依旧有记忆存留,但新时代的程序员可能没有必要再接触。本文就当做自留地,封存这段学习的记忆吧。

本文转载自微信公众号「BAT的乌托邦」

 

责任编辑:姜华 来源: BAT的乌托邦
相关推荐

2021-10-19 08:23:43

JMXJava 管理扩展

2021-10-11 08:51:50

JavaMailJDBCJava

2021-10-08 06:50:32

版本历史代码

2021-09-13 18:39:50

ServeltELJSP

2021-10-25 08:16:20

Java JAX-RS Java 基础

2017-10-31 12:56:52

Androidios谷歌

2009-07-07 14:33:44

JSP入门

2011-08-16 16:37:40

Oracle数据库树形查询根节点

2011-08-25 14:38:14

SQL Server修改表结构字段类型

2018-01-02 13:30:04

代码质量代码预言

2021-12-23 06:57:32

数据通信Websocket

2012-04-19 14:41:39

安腾惠普HP

2022-07-26 14:53:10

WebSocket网络通信协议

2010-01-28 16:01:18

Android Jni

2015-03-16 10:12:15

移动诈骗电信行业315晚会

2010-08-30 09:56:09

IP dhcp sno

2021-10-25 10:40:03

Java开发微服务

2010-02-05 10:23:09

C++基本函数

2010-04-15 14:12:20

Oracle 系统操作

2010-03-18 16:17:29

Python 变量类型
点赞
收藏

51CTO技术栈公众号