【方向盘】版本历史&代码示例之:WebSocket、JSTL

2022-11-03 16:04:11 浏览数 (1)

通透,是技术人应有的追求。 本文已被https://yourbatman.cn收录;女娲Knife-Initializr工程可公开访问啦;程序员专用网盘https://wangpan.yourbatman.cn;

你好,我是方向盘(YourBatman、方哥)。笔者的公号(Java方向盘)是保留地,只分享原创,不转载、不发商务广告!!!

✍前言

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

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

所属专栏
  • BATutopia-Java EE
相关下载
  • 工程源代码:https://github.com/yourbatman/FXP-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应用)

代码语言:javascript复制
<!-- javax命名空间版本(Tomcat 9.x及以下版本支持) -->
<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>

<!-- jakarta命名空间版本(Tomcat 10.x及以上版本支持) -->
<dependency>
    <groupId>jakarta.websocket</groupId>
    <artifactId>jakarta.websocket-api</artifactId>
    <version>2.0.0</version>
    <!-- <version>1.1.2</version> 此版本命名空间同javax -->
    <scope>provided</scope>
</dependency>

除此之外,一般情况下我们直接使用Web容器提供的Jar即可,如Tomcat
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-websocket</artifactId>
    <version>Tomcat版本号</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-websocket</artifactId>
    <version>Tomcat版本号</version>
</dependency>

✌版本历程

版本 | 发布日期 | JSR版本 | 对应Java EE版本 -------- | ----- | ----- | ----- | ----- WS 1.0 | 2013.06 | JSR 356 | Java EE 7 WS 1.1 | 2017.08 | JSR 356 | Java EE 8 WS 2.0 | 2020.11 | Jakarta管理 | Java EE 9

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

代码语言:javascript复制
<T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> handler) throws IllegalStateException;
<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端代码,提供一个服务端点:

代码语言:javascript复制
/**
 * 在此处添加备注信息
 *
 * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
 * @site https://yourbatman.cn
 * @date 2021/9/12 15:29
 * @since 0.0.1
 */
@ServerEndpoint("/websocket/chat")
public class WsServer {

    // 当前连接上来的连接们(每一个连接都是一个WsServerDemo实例,包含一个Session会话)
    private static Set<WsServer> webSocketSet = new CopyOnWriteArraySet<>();
    // 会话
    private Session session;

    /**
     * 建连成功的回调
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this); // 保存当前连接
        System.out.println("Server有新连接加入!当前在线人数为"   webSocketSet.size());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        System.out.println("Server有一连接关闭!当前在线人数为"   webSocketSet.size());
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String message) throws IOException {
        System.out.println("Server来自客户端的消息:"   message);
        sendMessage("会话["   session.getId()   "]的消息已经收到,内容为:"   message);
        // // =======群发消息=========
        // for (WsServerDemo item : webSocketSet) {
        //     try {
        //         item.sendMessage(message);
        //     } catch (IOException e) {
        //         continue;
        //     }
        // }
    }

    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Throwable error) {
        System.out.println("Server发生错误:"   error.getMessage());
    }

    /**
     * 发送消息
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

}

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

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

代码语言:javascript复制
/**
 * 在此处添加备注信息
 *
 * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
 * @site https://yourbatman.cn
 * @date 2021/9/12 15:31
 * @since 0.0.1
 */
@ClientEndpoint
public class WsClient {

    // 会话(与服务端建立的会话)
    private Session session;

    /**
     * 建连成功的回调
     */
    @OnOpen
    public void onOpen(Session session) throws IOException {
        this.session = session;
        System.out.println("Client连接到服务端成功,会话ID:"   session.getId());
        sendMessage("这是一条来自Client端,会话[" session.getId() "]的消息");
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("Client端收到消息: "   message);
    }

    @OnClose
    public void onClose() {
        System.out.println("Client会话"   session.getId()   "已断开");
    }

    /**
     * 发送消息
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
}

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

代码语言:javascript复制
public class ClientApp {

    private static URI uri = URI.create("ws://localhost:8080/websocket/chat");
    private static Session session;

    public static void main(String[] args) throws DeploymentException, IOException, InterruptedException {
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();

        // 顺序执行5次会话,端口后再建立新会话
        for (int i = 0; i < 5; i  ) {
            session = container.connectToServer(WsClient.class, uri);
            session.close();
            TimeUnit.SECONDS.sleep(2);
        }
    }
}

client端控制台日志:

代码语言:javascript复制
Client连接到服务端成功,会话ID:1
Client端收到消息: 会话[0]的消息已经收到,内容为:这是一条来自Client端,会话[1]的消息
Client会话1已断开
Client连接到服务端成功,会话ID:2
Client端收到消息: 会话[1]的消息已经收到,内容为:这是一条来自Client端,会话[2]的消息
Client会话2已断开
Client连接到服务端成功,会话ID:3
Client端收到消息: 会话[2]的消息已经收到,内容为:这是一条来自Client端,会话[3]的消息
Client会话3已...

server端控制台日志:

代码语言:javascript复制
Server有新连接加入!当前在线人数为1
Server来自客户端的消息:这是一条来自Client端,会话[1]的消息
Server有一连接关闭!当前在线人数为0
Server有新连接加入!当前在线人数为1
Server来自客户端的消息:这是一条来自Client端,会话[2]的消息
Server有一连接关闭!当前在线人数为0
Server有新连接加入!当前在线人数为1
Server来自客户端的消息:这是一条来自Client端,会话[3]的消息
Server有一连接关闭!当前在线人数为0

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

工程源代码:https://github.com/yourbatman/FXP-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" %>,支持的常用函数有:
代码语言:javascript复制
1. <c:out>:用于在页面输出
	- <c:out value="expression" default="expression" escapeXml="boolean"/>
2. <c:if>:逻辑判断
	- <c:if test="expression" var="name" scope="scope">  
	      body content  
	  </c:if>
3. <c:choose>:逻辑判断。when和otherwise的父标签
4. <c:when>: <c:otherwise>:
	- <c:choose>  
	    <c:when test="expression">  
	        body content  
	    </c:when>  
		...
	    <c:otherwise>  
		    body content  
		</c:otherwise>  
	  </c:choose>
	
5. <c:foreach>:
	- <c:forEach var="name" items="expression" varStatus="name" begin="expression" end="expression" step="expression">  
	      body content  
	  </c:forEach>

6. <c:url>:使用可选的查询参数创造一个URL地址
7. <c:set>:设置数据
8. <c:remove>:删除数据
  • 格式化标签:可对数字、日期时间等格式化。<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>,主要函数:
代码语言:javascript复制
1. <fmt:formatNumber>:格式化数字
2. <fmt:parseNumber>:解析字符串到数字、货币、百分比
3. <fmt:formatDate>:
4. <fmt:parseData>:
  • JSTL函数:一般用于辅助标签控制行为。<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>,主要函数:
代码语言:javascript复制
1. fn:contains:判断字符串是否包含另外一个字符串	
	- <c:if test="${fn:contains(name, searchString)}">
2. fn:indexOf:子字符串在母字符串中出现的位置	
	- ${fn:indexOf(name, '-')}
3. fn:toLowerCase:转为小写
	- ${fn.toLowerCase(product.name)}
  • SQL标签,<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>。主要函数:
代码语言:javascript复制
1. <sql:setDataSource>、<sql:query>、<sql:update>
  • XML标签,<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>。主要函数:
代码语言:javascript复制
1. <x:parse>、<x:if>、<x:forEach>

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

代码语言:javascript复制
<!-- javax命名空间版本(Tomcat 9.x及以下版本支持) -->
<dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>

<!-- jakarta命名空间版本(Tomcat 10.x及以上版本支持) -->
<dependency>
    <groupId>jakarta.servlet.jsp.jstl</groupId>
    <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
    <version>2.0.0</version>
    <!-- <version>1.2.7</version> 此版本命名空间同javax -->
</dependency>

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

✌版本历程

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

版本

发布日期

JSR版本

对应JSP版本

对应Servlet版本

JSTL 1.0

2002.07

JSR 52

JSP 1.2

Servlet 2.3

JSTL 1.1

2003.11

JSR 52

JSP 2.0

Servlet 2.4

JSTL 1.2

2006.05

JSR 52

JSP 2.1

Servlet 2.5

JSTL 2.0

2020.11

Jakarta管理

JSP 3.0

Servlet 5.0

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

✌生存现状

同JSP。

✌实现(框架)

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

✌代码示例

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

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

✍总结

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

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

本专栏文章

  • 【方同学】版本历史&代码示例之:Servelt、JSP、EL表达式
  • 【方同学】Java EE几十种技术,“活着的”还剩几何(Web应用技术篇)
  • 【方同学】Java EE几十种技术,“活着的”还剩几何(企业应用技术篇)

我是方向盘(YourBatman):前25年不会写Hallo World、早已毕业的大龄程序员。高中时期《梦幻西游》骨灰玩家,网瘾失足、清考、延期毕业、房产中介、保险销售、送外卖…是我不可抹灭的黑标签