• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

【Tomcat 源码系列】认识 Tomcat

JAVA相关 楷哥 2061次浏览 0个评论

一,前言

说一句大实话,“平时一直在用 Tomcat,但是我从来没有用过 Tomcat”。

“平时一直在用 Tomcat”,是因为搬砖用的 SpringBoot,内嵌了 Tomcat,每次启动程序的时候,都需要启动 Tomcat。

“我从来没有用过 Tomcat”,是因为没有专门去用过 Tomcat,没有写过 Servlet,没有写过 JSP,没有配置过 Tomcat。

这篇博客介绍如何使用 Tomcat,根据官方提供的例子,分析如何写 Servlet 程序,JSP 页面,WebSocket 程序。

在继续源码之前,不妨先用用 Tomcat 吧。代码请看这里:https://github.com/zzk0/tomcat-example

二,Tomcat

2.1 运行 Tomcat

首先点击这里去下载一个 Tomcat 先吧。

解压一下,我们来看看里面都有些什么东西。

bin: 启动关闭脚本等
conf: 配置文件,server.xml 服务器配置,web.xml 应用配置
lib: Tomcat 的包,比如有 catalina.jar
logs: 日志
temp: 临时文件
webapps: 存放网站应用(webapp),一个文件夹对应一个 webapp,在域名端口后面,输入文件夹名字就可以访问对应的 webapp,比如 localhost:8080/examples
work: Tomcat 的工作目录,不断点进去,会发现一些 .class 文件,这些对应动态生成的页面。

【Tomcat 源码系列】认识 Tomcat

进入 bin 目录,点击 startup 脚本。启动之后,界面显示如下。

【Tomcat 源码系列】认识 Tomcat

进入 work 目录,不断深入。我们可以发现有一个 index_jsp.java 及其 class 文件。

【Tomcat 源码系列】认识 Tomcat

用 IDE 看看 index_jsp.java,看 _jspService 方法,里面有很多 out.write,而写出去的内容正是我们上面看到的网页。这启示我们,其实 JSP 的原理就是生成 java 文件,并通过 out.write 写到网页中,因此可以将一些变量动态的写入到网页,而不是只能看到一个静态的 html。

【Tomcat 源码系列】认识 Tomcat

2.2 Tomcat 概念和结构

有一些基本概念需要理解,请看这里。这些概念有:Server,Service,Engine,Host,Context,Wrapper,Pipeline,Valve,Realm,Connector。名词很多,知道个大概意思和作用就行了。

下面这个图就清晰地展示了 Tomcat 的结构图,仔细去看 conf/server.xml 这个文件的 xml 树结构。一个 Server 可以跑多个 Service,默认配置了一个名字为 Catalina 的 Service,这个 Service 下面可以配置多个 Connector 和 一个 Engine。这个 Connector 负责监听端口,并将客户端请求转发给 Engine。一个 Engine 可以有多个 Host,每个 Host 对应一个站点。一个 Host 中可以有多个 Context,一个 Context 对应于一个应用。

【Tomcat 源码系列】认识 Tomcat

一张更全的结构图。一个请求,从 Connector 进来,通过 Pipeline 进入 Engine,再进入 Host、Context,最终找到对应的 Servlet 然后进行调用。

【Tomcat 源码系列】认识 Tomcat

三,例子

运行 startup,输入 http://localhost:8080/examples/ 查看官方的例子。

官方提供了三类例子,分别是 Servlet,JSP,WebSocket 的例子。我们可以点进去看看 Tomcat 能够做什么。后面我们来开发一下自己的 Servlet,JSP,WebSocket 程序,看看这些程序是如何创建的。

那么这些例子在哪里呢?我们可以进入到 webapps 目录下面。我们可以看到有 examples。一个目录对应一个网站应用,比如 examples,我们可以用 http://localhost:8080/examples/ 来访问。对于 ROOT,可以直接用域名和端口访问。

【Tomcat 源码系列】认识 Tomcat

进入 examples 目录,我们看看一个 webapp 有哪些组成部分。其中 WBE-INF 里面包含了网站的配置,类文件。META-INF 是打包的时候,提供的元数据。

【Tomcat 源码系列】认识 Tomcat

四,自己动手

3.1 开发和部署

我们怎么开发一个 Tomcat 的 webapp 呢?开发完了之后,又需要如何部署呢?我们需要配置哪些东西呢?

接下来,我们用 IDEA 来开发和部署。我用的版本是:IntelliJ IDEA 2020.2.1 (Ultimate Edition)。

建项目

首先我们来新建一个项目,使用 Gradle 来构建,勾选 Web。

【Tomcat 源码系列】认识 Tomcat

设置项目名称。

【Tomcat 源码系列】认识 Tomcat

在 build.gradle 中引入下面的依赖,我用的是 Tomcat 10,所以需要引入 Jakarta 开头的包,如果你用的是别的版本的 Tomcat,请自行找到对应版本的包。

// https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api
providedCompile group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '5.0.0'

// https://mvnrepository.com/artifact/jakarta.websocket/jakarta.websocket-api
providedCompile group: 'jakarta.websocket', name: 'jakarta.websocket-api', version: '2.0.0'

配置项目

点击右上角,添加配置。

【Tomcat 源码系列】认识 Tomcat

添加 Tomcat Server,注意不要选到后面的 TomcatEE 版本了。选择 Local 版本。

【Tomcat 源码系列】认识 Tomcat

点击 Configure 按钮,找到 Tomcat 解压目录即可。不需要进入到 bin 当中。我们还可以看到左下角有个 Warning,它提示你需要配置部署。于是,我们选中 Deployment,去配置。

【Tomcat 源码系列】认识 Tomcat

点击那个加号,然后选择 exploded 版本。

【Tomcat 源码系列】认识 Tomcat

点击 ok 之后,修改 Application Context,这个 Context 用来配置访问时候 url 的名字。可以理解为这个 webapp 的名字。之后,我们可以使用 localhost:8080/example 来访问。

【Tomcat 源码系列】认识 Tomcat

至此,我们的第一个 webapp 就配置好了。

3.2 JSP

接下来,展开 src,main,webapp,找到 index.jsp。我们可以在这里开始写代码。

【Tomcat 源码系列】认识 Tomcat

编辑内容,注意到下面有 java 代码,其实 jsp 就是 html 和 java 的混合体。下面的 jsp,就是向浏览器输出了 Hello World 这个字符串。我们点击运行,启动一下。这里就不再展开 JSP 了,如果又需要再去学一学吧。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <%
      String s = "Hello World";
      out.write(s);
    %>
  </body>
</html>

可以看到 Hello World 了。

【Tomcat 源码系列】认识 Tomcat

3.3 Servlet

接下来,我们来写第一个 Servlet 程序。写个鬼咧,写代码是不可能写的,这辈子都不会写代码。直接从 webapps\examples\WEB-INF\classes 中复制一个过来。你也可以复制我的代码。

【Tomcat 源码系列】认识 Tomcat

下面这段代码,可以视为一个 Servlet,它接收 GET 请求,并将一个 html 逐行逐行写给前端。因为 Java 代码里面太多这些 out.println 了,导致要修改前端必须要改 Java,这样不好。因此,才有了 JSP。

import java.io.*;
import jakarta.servlet.*;
import jakarta.servlet.http.*;

public class ExampleServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

接下来,我们还要配置,如何去调用这个 Servlet 程序。在 webapp 下面新建文件夹 WEB-INF,并在下面新建一个 web.xml 文件。

【Tomcat 源码系列】认识 Tomcat

同样,我去找一份配置,这次我在 webapps/ROOT 下面到 web.xml,然后添加一些信息来配置 url。servlet 标签定义了一个 servlet 的名字及其所在地点。这个 servlet-class 需要根据包的路径来,前面我新建的 ExampleServlet 并没有包,所以直接这样子配就行。配好了 servlet,还要去配调用这个 servlet 的 URL。

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0"
         metadata-complete="true">

    <display-name>Welcome to Tomcat</display-name>
    <description>
        Welcome to Tomcat
    </description>

    <servlet>
        <servlet-name>ExampleServlet</servlet-name>
        <servlet-class>ExampleServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>ExampleServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

点击启动,访问这个链接 http://localhost:8080/example/hello

【Tomcat 源码系列】认识 Tomcat

3.4 WebSocket

接下来,我们参考官方的例子,搞一个基于 WebSocket 的聊天室。不写代码,全靠复制粘贴。

我们需要从 \webapps\examples\WEB-INF\classes\websocket\chat 复制代码。

【Tomcat 源码系列】认识 Tomcat

将下面代码复制到 ChatAnnotation 中,@ServerEndpoint 用来配置提供 websocket 协议服务的端点,它支持服务端推送消息。

import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

@ServerEndpoint(value = "/websocket/chat")
public class ChatAnnotation {

    private static final String GUEST_PREFIX = "Guest";
    private static final AtomicInteger connectionIds = new AtomicInteger(0);
    private static final Set<ChatAnnotation> connections =
            new CopyOnWriteArraySet<>();

    private final String nickname;
    private Session session;

    public ChatAnnotation() {
        nickname = GUEST_PREFIX + connectionIds.getAndIncrement();
    }


    @OnOpen
    public void start(Session session) {
        this.session = session;
        connections.add(this);
        String message = String.format("* %s %s", nickname, "has joined.");
        broadcast(message);
    }


    @OnClose
    public void end() {
        connections.remove(this);
        String message = String.format("* %s %s",
                nickname, "has disconnected.");
        broadcast(message);
    }


    @OnMessage
    public void incoming(String message) {
        // Never trust the client
        String filteredMessage = String.format("%s: %s",
                nickname, message.toString());
        broadcast(filteredMessage);
    }




    @OnError
    public void onError(Throwable t) throws Throwable {
    }


    private static void broadcast(String msg) {
        for (ChatAnnotation client : connections) {
            try {
                synchronized (client) {
                    client.session.getBasicRemote().sendText(msg);
                }
            } catch (IOException e) {
                connections.remove(client);
                try {
                    client.session.close();
                } catch (IOException e1) {
                    // Ignore
                }
                String message = String.format("* %s %s",
                        client.nickname, "has been disconnected.");
                broadcast(message);
            }
        }
    }
}

然后,我们再从 \webapps\examples\websocket 偷一个 chat.xhtml 文件。放到 webapp 下面就好了。

【Tomcat 源码系列】认识 Tomcat

之后还需要修改 chat.xhtml 中 websocket 的端点。将下面红框中的东西,改成一开始 IDEA 启动配置中的 Application Context。在这里,我们只需要去掉 s 就好了。

【Tomcat 源码系列】认识 Tomcat

接下来启动!

通过这个地方访问聊天室:http://localhost:8080/example/chat.xhtml

【Tomcat 源码系列】认识 Tomcat

发送的消息,都可以即时被推送。

【Tomcat 源码系列】认识 Tomcat

五,总结

这篇博客展示了如何使用 Tomcat,开发使用 Servlet,JSP,WebSocket 的 Demo。

总结一下,Tomcat 就是一个实现了 Servlet,JSP,WebSocket 规范的 HTTP 服务器。上面展示了使用这些技术的例子,要明白这背后做了什么,还得了解这些技术的规范,还要去看实现,看 Tomcat 源码。


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明【Tomcat 源码系列】认识 Tomcat
喜欢 (0)

您必须 登录 才能发表评论!

加载中……