初阶JavaEE(14)表白墙程序

接上次博客:初阶JavaEE(13)(安装、配置:Smart Tomcat;访问出错怎么办?Servlet初识、调试、运行;HttpServlet:HttpServlet;HttpServletResponse)-CSDN博客

目录

表白墙程序

“表白墙”的前后端交互:

提交信息接口:

获取信息接口:

我们先来编写提交消息的代码:

当前我们已经把数据提交到服务器保存了,但是我们还需要能够把服务器的数据拿回到客户端页面并显示。

数据存入数据库

1、在 pom.xml 中引入 mysql 的依赖:

2、建库建表:

3、编写数据库代码:


表白墙程序

我们可以先来一个最简单的网站,只有一个页面——“表白墙”。

类似这样:

网站 = 前端+前后端交互+后端

这个“表白墙 ”的前端部分直接提供给大家,我们现在主要是关注后面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙</title>
    <style>
        /* * 通配符选择器, 是选中页面所有元素 */
        * {
            /* 消除浏览器的默认样式. */
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 600px;
            margin: 20px auto;
        }

        h1 {
            text-align: center;
        }

        p {
            text-align: center;
            color: #666;
            margin: 20px 0;
        }

        .row {
            /* 开启弹性布局 */
            display: flex;
            height: 40px;
            /* 水平方向居中 */
            justify-content: center;
            /* 垂直方向居中 */
            align-items: center;
        }

        .row span {
            width: 80px;
        }

        .row input {
            width: 200px;
            height: 30px;
        }

        .row button {
            width: 280px;
            height: 30px;
            color: white;
            background-color: orange;
            /* 去掉边框 */
            border: none;
            border-radius: 5px;
        }

        /* 点击的时候有个反馈 */
        .row button:active {
            background-color: grey;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>表白墙</h1>
        <p>输入内容后点击提交, 信息会显示到下方表格中</p>
        <div class="row">
            <span>谁: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>对谁: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>说: </span>
            <input type="text">
        </div>
        <div class="row">
            <button id="submit">提交</button>
        </div>
        <!-- <div class="row">
            xxx 对 xx 说 xxxx
        </div> -->
    </div>

    <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
        }
    </script>
</body>
</html>

我们现在简单分析一下这段代码:

这段HTML代码定义了一个表白墙页面,其中包含一些HTML元素和与之相关的JavaScript代码。以下是对代码的分析:

  1. HTML文档的基本结构:页面中的HTML,包括<!DOCTYPE>声明、<html>根元素、<head>头部和<body>主体部分。

  2. <head>部分,包括了一些<meta>标签用于设置文档的字符编码和视口配置。还包括了页面标题("表白墙")以及内联的CSS样式定义。

  3. <style>标签内,定义了一些CSS规则,用于样式化页面元素。这些规则包括:

    • 消除浏览器默认样式。
    • 定义一个容器(.container)的宽度和居中对齐。
    • 设置标题(h1)和提示文字(p)的文本对齐、颜色和间距。
    • 定义了具有弹性布局的行(.row),水平和垂直居中,以及子元素(spaninputbutton)的样式,包括宽度、高度和颜色。
    • 设置了按钮的点击反馈效果。
    •     <style>
              /* * 通配符选择器, 是选中页面所有元素 */
              * {
                  /* 消除浏览器的默认样式. */
                  margin: 0;
                  padding: 0;
                  box-sizing: border-box;
              }
      
              .container {
                  width: 600px;
                  margin: 20px auto;
              }
      
              h1 {
                  text-align: center;
              }
      
              p {
                  text-align: center;
                  color: #666;
                  margin: 20px 0;
              }
      
              .row {
                  /* 开启弹性布局 */
                  display: flex;
                  height: 40px;
                  /* 水平方向居中 */
                  justify-content: center;
                  /* 垂直方向居中 */
                  align-items: center;
              }
      
              .row span {
                  width: 80px;
              }
      
              .row input {
                  width: 200px;
                  height: 30px;
              }
      
              .row button {
                  width: 280px;
                  height: 30px;
                  color: white;
                  background-color: orange;
                  /* 去掉边框 */
                  border: none;
                  border-radius: 5px;
              }
      
              /* 点击的时候有个反馈 */
              .row button:active {
                  background-color: grey;
              }
          </style>
  4. <body>部分,创建了一个容器(.container,选择器,里面是样式属性),其中包括:

    • 一个标题(<h1>,标签)。
    • 一段提示文字(<p>,标签)。
    • 四个输入行(.row,类选择器):分别用于输入谁(from)、对谁(to)、说什么(message)。
    • 一个提交按钮(<button>)。
    • CSS中选择器的种类非常多,目的就是选中某个/某些标签,后面的{ }就是在描述这个标签应该有的样式。上面的CSS代码包含了多个CSS选择器和相应的样式规则,用于定义页面中不同元素的外观和排版。以下是对其中的一些选择器和规则的解释:
    • 通配符选择器 (*):

      • *选择器用于选择页面中的所有元素。
      • 在这个例子中,通配符选择器被用来消除浏览器默认样式,将页面中所有元素的marginpaddingbox-sizing属性设置为特定的值。
    • 类选择器 (.container, .row):

      • 类选择器以.开头,用于选择具有特定类名的元素。
      • 例如,.container选择器选择所有具有container类的元素,.row选择器选择所有具有row类的元素。
      • 这些选择器用于样式化容器和行的元素,以设置宽度、居中等样式属性。
    • 元素选择器 (h1, p):

      • 元素选择器用于选择特定类型的HTML元素,如h1p
      • 例如,h1选择器选择所有<h1>标题元素,p选择器选择所有<p>段落元素。
      • 这些选择器用于设置文本对齐、颜色等样式属性。
    • 后代选择器 (.row span, .row input):

      • 后代选择器选择元素的后代元素,即在特定元素内部的元素。
      • 例如,.row span选择器选择.row类下的<span>元素,.row input选择器选择.row类下的<input>元素。
      • 这些选择器用于设置特定元素内部的样式属性。
    • 伪类选择器 (.row button:active):

      • 伪类选择器用于选择元素的特定状态或条件。
      • 例如,.row button:active选择器选择.row类下的<button>元素在被激活(鼠标点击)时的状态。
      • 这些选择器用于为元素的不同状态定义样式。
    • 里面的
              .row {
                  /* 开启弹性布局 */
                  display: flex;
                  height: 40px;
                  /* 水平方向居中 */
                  justify-content: center;
                  /* 垂直方向居中 */
                  align-items: center;
              }
      

      对应调用:

      <body>
          <div class="container">
              <h1>表白墙</h1>
              <p>输入内容后点击提交, 信息会显示到下方表格中</p>
              <div class="row">
                  <span>谁: </span>
                  <input type="text">
              </div>
              <div class="row">
                  <span>对谁: </span>
                  <input type="text">
              </div>
              <div class="row">
                  <span>说: </span>
                  <input type="text">
              </div>
              <div class="row">
                  <button id="submit">提交</button>
              </div>
              <!-- <div class="row">
                  xxx 对 xx 说 xxxx
              </div> -->
          </div>
  5. <script>标签中包含了JavaScript代码,这个代码就是进行前端编写逻辑的核心部分。HTML和CSS都属于描述性的语言,能够描述一个界面上有什么东西,长什么样子,但是并不表示逻辑。JS才能描述逻辑,通过这些逻辑我们主要做两件事:1、网页和用户的交互;2、网页和后端服务器的交互。该代码实现了以下功能:

        <script>
            // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
            // 点击的时候, 获取到三个输入框中的文本内容
            // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
            let containerDiv = document.querySelector('.container');
            let inputs = document.querySelectorAll('input');
            let button = document.querySelector('#submit');
            button.onclick = function() {
                // 1. 获取到三个输入框的内容
                let from = inputs[0].value;
                let to = inputs[1].value;
                let msg = inputs[2].value;
                if (from == '' || to == '' || msg == '') {
                    return;
                }
                // 2. 构造新 div
                let rowDiv = document.createElement('div');
                rowDiv.className = 'row message';
                rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
                containerDiv.appendChild(rowDiv);
                // 3. 清空之前的输入框内容
                for (let input of inputs) {
                    input.value = '';
                }
            }
            let revertButton = document.querySelector('#revert');
            revertButton.onclick = function() {
                // 删除最后一条消息. 
                // 选中所有的 row, 找出最后一个 row, 然后进行删除
                let rows = document.querySelectorAll('.message');
                if (rows == null || rows.length == 0) {
                    return;
                }
                containerDiv.removeChild(rows[rows.length - 1]);
            }
        </script>
            let containerDiv = document.querySelector('.container');
            let inputs = document.querySelectorAll('input');
            let button = document.querySelector('#submit');
    • JS使用let关键字定义变量,不需要写类型,变量的类型是由后面的初始化的表达式来确定的。document是浏览器提供的全局变量。浏览器给JS提供了很多API,其中一部分API就是通过document全局变量提供的。上述代码后面的query Selector就是一个很重要的API,选中指定的HTML元素,参数就是CSS选择器,通过选择器定位到元素。后面的querySelectorAll表示选中所有的imput标签,获取到标签目的是得到输入框用户里写的内容。后面的submit表示选中按钮。
    • 当用户点击"提交"按钮(button)时,获取三个输入框的文本内容,并构造一个新的div.row元素,将内容显示在这个div中。
    • 如果输入框中的任何一个内容为空,提交操作不会执行。
    • 在构造新消息后,清空输入框的内容。
    • 总的来说,开头这几行代码是要选中页面的元素,具体是怎么选中的?API是什么意思?选择器是什么意思?……这些其实对于我们现在都不重要。我们选中它们就是后续要操作元素:一个是“获取元素中的内容”,比如对于input输入框就可以拿到用户输入的字符串。还有就是“修改元素中的内容”。

    •         button.onclick = function() {
                  // 1. 获取到三个输入框的内容
                  let from = inputs[0].value;
                  let to = inputs[1].value;
                  let msg = inputs[2].value;
                  if (from == '' || to == '' || msg == '') {
                      return;
                  }
                  // 2. 构造新 div
                  let rowDiv = document.createElement('div');
                  rowDiv.className = 'row message';
                  rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
                  containerDiv.appendChild(rowDiv);
                  // 3. 清空之前的输入框内容
                  for (let input of inputs) {
                      input.value = '';
                  }
              }

      给按钮注册一个点击事件的回调函数,点击按钮就会执行这个函数。里面value拿到输入框的内容,后面点击之后能够显示出内容,就是需要在页面中创建出新的div。

总的来说,这段HTML代码创建了一个简单的页面,允许用户输入表白信息,单击提交按钮后将这些信息显示在页面上。

通过上述介绍,你只需要知道:

  1. 前端的构成

    • 前端开发通常包括HTML(页面的结构)、CSS(页面的样式)和JavaScript(页面的交互)。
    • HTML用于定义页面的结构,如标题、段落、表格等。
    • CSS用于样式化页面,包括颜色、字体、布局等。
    • JavaScript用于处理页面的交互,例如用户输入、按钮点击等。
  2. JavaScript交互

    • JavaScript在前端开发中用于添加交互性。它可以找到HTML元素(DOM元素)并通过API操作这些元素的属性和内容。
    • 操作DOM元素包括获取元素、修改元素的文本内容、样式、添加事件处理程序等。
  3. 基本编程概念

    • 前端开发涉及到基本的编程概念,如变量定义、条件语句、循环、表达式、运算符和函数。
    • 这些概念是通用的,可以应用于JavaScript以及其他编程语言。

虽然前端开发可能更加复杂,但这些基本概念和流程是入门前端开发的重要基础。理解这些概念可以帮助我们更好地理解前端代码和进行更高级的开发工作,并且为我们后续的重点做一个理解的准备。

虽然这里看起来好像是已经构建成功了,但是现在这里的数据都是在浏览器的内存中保存到,刷新/关闭页面就没了!我们希望这里的数据是可以长期存在的。

而且我们希望不同的浏览器/页面都可以看到同一个数据。 

此处服务器要实现的功能主要是两个页面:

1、页面加载的时候,网页要从服务器这里获取到表白数据(让网页端给服务器发起HTTP请求,服务器返回响应里就带着刚才的数据)

2、点击提交的时候网页就要把用户输入的信息发送到服务器这边,让服务器负责保存。

所以,在一个网站中,服务器起到的最主要的效果往往就是“存储数据”。

当然,光“存储”还不够,服务器这边也就需要能够提供两种风格的接口,存数据和取数据。

服务器端应用程序需要包括以下关键部分:

  1. 路由:定义用于处理客户端请求的不同端点(URL)和HTTP请求方法(GET、POST等)的路由。例如,一个路由可以用于获取表白数据,另一个用于接受用户提交的表白信息。

  2. 数据库:用于存储表白数据的数据库。服务器应该能够将用户提交的数据存储在数据库中,并能够从数据库中检索数据以供客户端获取。

  3. API:服务器应提供API端点,使客户端能够通过HTTP请求与服务器进行通信。例如,通过GET请求获取数据,通过POST请求将新数据发送到服务器。API应负责处理这些请求,执行适当的数据库操作,并返回响应。

  4. 前后端通信:客户端需要使用JavaScript通过HTTP请求与服务器进行通信。客户端代码将发送请求到服务器的API端点,接收服务器的响应,并根据响应更新页面。

  5. 持久化存储:服务器端应确保数据在服务器上进行持久化存储,以便不同的客户端和页面可以访问和共享相同的数据。

“表白墙”的前后端交互:

使用服务器目的是为了能够在服务器这边存储用户提交的信息,并能够把信息获取下来,所以服务器这边就需要给网页提供两个HTTP接口:

提交信息接口:

客户端请求:

  • 当用户点击提交按钮时,客户端会触发一个Ajax POST请求,将用户输入的信息提交给服务器。

请求示例:

POST /message

请求数据示例:

{
    "from": "张三",
    "to": "李四",
    "message": "我喜欢你!!!"
}

服务器响应:

  • 服务器接收到请求后,会返回一个HTTP响应,状态码为200 OK,并使用Content-Type标头指定响应数据为JSON格式。

获取信息接口:

客户端请求:

  • 当网页加载时,客户端会发起一个Ajax GET请求到服务器,请求获取已提交的信息。

请求示例:

GET /message

服务器响应:

  • 服务器接收到请求后,会返回一个HTTP响应,状态码为200 OK,并使用Content-Type标头指定响应数据为JSON格式。

响应示例:

HTTP/1.1 200 OK
Content-Type: application/json

[
    {
        "from": "张三",
        "to": "李四",
        "message": "我喜欢你!!!"
    },
    {
        "from": "小米",
        "to": "旺仔",
        "message": "我喜欢你!!!"
    },
    {
        "from": "虹猫",
        "to": "蓝兔",
        "message": "我喜欢你!!!"
    },
    // ...
]

这两个接口允许用户从客户端向服务器提交表白信息,同时也允许客户端获取已提交的信息,从而实现"表白墙"功能。

这些HTTP接口的具体设计可以根据需求进行自定义,但一旦确定了接口的设计,它将成为前后端交互的约定。前端需要编写代码来构造HTTP请求并解析HTTP响应,而后端需要编写代码来解析HTTP请求并构造HTTP响应。这个过程就涉及到自定义应用层协议的设计。

虽然具体的前后端交互数据格式可以有多种设计方式,但在开始编写代码之前,确保明确接口设计方式是非常重要的。这将成为后续开发的基础。

编写前端代码:构造HTTP请求、解析HTTP响应;

编写后端代码:解析HTTP请求、构造HTTP响应。

这个过程就是自定义应用层协议。

虽然说这里的具体的前后端交互数据的格式怎么设计都行,存在无数种设计方式,但是我们现在还是来明确一下具体的设计方式吧……

既然我们现在已经约定好了前后端交互的接口,接下来我们就可以开始编写代码了:

(1)先写前端代码、发送请求,提交消息;

(2)再写后端代码,解析请求,构造响应;

(3)最后再写前端代码,解析响应;

我们先来编写提交消息的代码:

我们创建一个新的项目,引入依赖、创建目录,然后把刚才的网页放到webapp目录里面:

创建一个Tomcat来看看: 

 

于是此处我们就通过Tomcat网络的形式把这个HTML加载出来了: 

接下来就是编写我们的前端代码、发送HTTP POST请求:

你需要打开原网页编写:

我们通过VSCode打开: 

关于VSCode,它的下载安装视频都可以在b站上找到,它的国内下载速度贼慢,我通过一个博主的视频知道了一种快速下载的方法,你可以看看这个视频一起学习一下:

解决vscode国内下载速度过慢问题_哔哩哔哩_bilibili

下载完成之后,安装视频也顺便看看,你也可以安装一些插件以获得更好的使用体验: 

说回来,打开: 

首先我们需要引入一个Jquery ,这样我们才能够使用Ajax发起请求:

 

 

https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js

 前端引入第三方库,往往就是通过script标签写一个地址即可:

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

这个代码在点击按钮的回调函数中,会在点击按钮之后被调用到: 

            // 4. 把用户填写的内容, 发送给服务器. 让服务器来保存. 
            //    $ 是 jquery 提供的全局变量. ajax 就是 $ 的一个方法. 
            //    ajax 的参数是一个 js 对象, 可以有很多属性
            let body = {
                "from": from,   // from 变量里的值, 就是第一个输入框的内容, "张三"
                "to": to,       // to 变量的值, 就是第二个输入框的内容, "李四"
                "message": msg  // msg 变量的值, 就是第三个输入框的内容, "我喜欢你很久了"
            };
            // 上述 body 是一个 js 对象, 还需要转成 json 字符串. 
            let jsonString = JSON.stringify(body);
            $.ajax({
                type: 'post',
                url: 'message',
                contentType: 'application/json; charset=utf8',
                data: jsonString,
                success: function(body) {
                    // 这个回调就是收到响应之后要执行的代码了. 
                }
            });
        }

 JSON.stringify() 是 JavaScript 中的一个方法,用于将 JavaScript 对象或值转换为 JSON 格式的字符串。这个方法接受一个对象或值作为参数,并返回一个包含 JSON 字符串表示的对象的方法。

例如,如果你有一个 JavaScript 对象:

const person = {
  name: "John",
  age: 30,
  city: "New York"
};

可以使用 JSON.stringify() 将它转换为 JSON 字符串:

const jsonString = JSON.stringify(person);

 jsonString 现在包含以下 JSON 字符串:

{"name":"John","age":30,"city":"New York"}

这个方法非常有用,因为它允许我们在不同的应用程序和平台之间轻松传递和解析数据。在前后端通信中,通常会使用 JSON 格式来交换数据,而 JSON.stringify() 可以将对象转换为 JSON 字符串,以便在 HTTP 请求中发送给服务器或在前端和后端之间传递数据。

注意,JSON.stringify() 方法还可以接受第二个参数,用于指定要包含在生成的 JSON 字符串中的属性或过滤函数,以及缩进选项,以使生成的 JSON 字符串更易于阅读。

 还有一点:

          $.ajax({
                type: 'post',
                url: 'message',
                contentType: 'application/json; charset=utf8',
                data: jsonString,
                success: function(body) {
                    // 这个回调就是收到响应之后要执行的代码了. 
                }

前端Ajax请求的URL路径,写作“message”,前面没加“/”,此时这是一个相对路径的写法。

后端处理Ajax请求,URL路径写作“/message”,前面是要加“/”的,此时是Servlet要求的写法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙</title>
    <style>
        /* * 通配符选择器, 是选中页面所有元素 */
        * {
            /* 消除浏览器的默认样式. */
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 600px;
            margin: 20px auto;
        }

        h1 {
            text-align: center;
        }

        p {
            text-align: center;
            color: #666;
            margin: 20px 0;
        }

        .row {
            /* 开启弹性布局 */
            display: flex;
            height: 40px;
            /* 水平方向居中 */
            justify-content: center;
            /* 垂直方向居中 */
            align-items: center;
        }

        .row span {
            width: 80px;
        }

        .row input {
            width: 200px;
            height: 30px;
        }

        .row button {
            width: 280px;
            height: 30px;
            color: white;
            background-color: orange;
            /* 去掉边框 */
            border: none;
            border-radius: 5px;
        }

        /* 点击的时候有个反馈 */
        .row button:active {
            background-color: grey;
        }
    </style>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
</head>
<body>
    <div class="container">
        <h1>表白墙</h1>
        <p>输入内容后点击提交, 信息会显示到下方表格中</p>
        <div class="row">
            <span>谁: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>对谁: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>说: </span>
            <input type="text">
        </div>
        <div class="row">
            <button id="submit">提交</button>
        </div>
        <!-- <div class="row">
            xxx 对 xx 说 xxxx
        </div> -->
    </div>

    <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
            // 4. 把用户填写的内容, 发送给服务器. 让服务器来保存. 
            //    $ 是 jquery 提供的全局变量. ajax 就是 $ 的一个方法. 
            //    ajax 的参数是一个 js 对象, 可以有很多属性
            let body = {
                "from": from,   // from 变量里的值, 就是第一个输入框的内容, "张三"
                "to": to,       // to 变量的值, 就是第二个输入框的内容, "李四"
                "message": msg  // msg 变量的值, 就是第三个输入框的内容, "我喜欢你很久了"
            };
            // 上述 body 是一个 js 对象, 还需要转成 json 字符串. 
            let jsonString = JSON.stringify(body);
            $.ajax({
                type: 'post',
                url: 'message',
                contentType: 'application/json; charset=utf8',
                data: jsonString,
                success: function(body) {
                    // 这个回调就是收到响应之后要执行的代码了. 
                }
            });
        }
    </script>
</body>
</html>

 通过以上代码,我们就可以构造并且发送一个请求。

服务器读取上述请求,并计算响应:

此时我们需要使用Jackson读取前端的数据:

//读取前端发来的数据,把这个数据保存到服务器这边.
resp.setContentType("application/json;charset=utf-8");
Message message = objectMapper.readValue(req.getInputStream(), Message.class);

既然如此,关于第二个参数也就是我们想要转化成的类,还需要构造一下:

public class Message {
    public String from;
    public String to;
    public String message;
}

注意,这里定义的Java这个类里面属性的名称要和我们之前约定好的json前端代码属性的名字保存一致:

package org.example;

public class Message {
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}
package org.example;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/message")
public class MessageServlet extends HttpServlet {

    // 用于保存所有的留言
    private List<Message> messageList = new ArrayList<Message>();
    // 用于转换 JSON 字符串
    private ObjectMapper objectMapper = new ObjectMapper();
    
    // 新增留言
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
        //读取前端发来的数据,把这个数据保存到服务器这边.
        resp.setContentType("application/json;charset=utf-8");
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        System.out.println("请求中收到的message"+message);
        // 最简单的办法, 直接在内存中保存. 我们可以使用一个集合类, 把所有收到的 message 都存到内存中.
        messageList.add(message);
        // 很明显, 保存到内存, 并非是一个非常合理的办法. 后续一旦重启服务器, 数据丢失了.
        // 相比之下, 把这个数据持久化存储到数据库中, 更科学的.
        resp.setStatus(200);
        resp.getWriter().write( "ok");
    }
}

回到前端代码,处理服务器返回的响应。

此处success回调函数不是立即执行的,而是在浏览器收到服务器返回的“成功”这样的响应之后才会执行到function。

这个函数就是来执行响应的,并且这个函数的第一个参数就是响应中body中的内容。 

注意区分:

算了,我们直接改一下名字来区分:

            // 4. 把用户填写的内容, 发送给服务器. 让服务器来保存. 
            //    $ 是 jquery 提供的全局变量. ajax 就是 $ 的一个方法. 
            //    ajax 的参数是一个 js 对象, 可以有很多属性
            let requestbody = {
                "from": from,   // from 变量里的值, 就是第一个输入框的内容, "张三"
                "to": to,       // to 变量的值, 就是第二个输入框的内容, "李四"
                "message": msg  // msg 变量的值, 就是第三个输入框的内容, "我喜欢你很久了"
            };
            // 上述 body 是一个 js 对象, 还需要转成 json 字符串. 
            let jsonString = JSON.stringify(requestbody);
            $.ajax({
                type: 'post',
                url: 'message',
                contentType: 'application/json; charset=utf8',
                data: jsonString,
                success: function(responsebody) {
                    // 这个回调就是收到响应之后要执行的代码了. 
                }
            });

 此时,由于我们刚刚返回的响应是“ok”,此处的body就是“ok”。

其实一般来说,为了和请求对应上,服务器返回的数据也会按照json格式来组织。

        resp.setStatus(200);
        resp.setContentType("application/json");
        resp.getWriter().write("{ \"ok\": true }");

现在我们先简单返回一个“ok”。

什么是开发者工具的控制台?

我们随便打开一个页面:

这样就写好了: 

 <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
            // 4. 把用户填写的内容, 发送给服务器. 让服务器来保存. 
            //    $ 是 jquery 提供的全局变量. ajax 就是 $ 的一个方法. 
            //    ajax 的参数是一个 js 对象, 可以有很多属性
            let requestbody = {
                "from": from,   // from 变量里的值, 就是第一个输入框的内容, "张三"
                "to": to,       // to 变量的值, 就是第二个输入框的内容, "李四"
                "message": msg  // msg 变量的值, 就是第三个输入框的内容, "我喜欢你很久了"
            };
            // 上述 body 是一个 js 对象, 还需要转成 json 字符串. 
            let jsonString = JSON.stringify(requestbody);
            $.ajax({
                type: 'post',
                url: 'message',
                contentType: 'application/json; charset=utf8',
                data: jsonString,
                success: function(responsebody) {
                     // 这个回调就是收到响应之后要执行的代码了. 
                    // 前端使用 console.log 打印日志到控制台. (chrome 开发者工具的控制台)
                    console.log("responseBody: " + responseBody);
                }
            });
        }

        //直接在script里面写的JS代码,就是在页面加载时被执行到的
        //发起一个get请求,从服务器获取到数据
        //get请求不需要body
    </script>

 我们现在就来运行一下看看效果:

注意,这里不可以直接在浏览器里面访问message这个路径,因为message这个路径当前是按照post请求来处理的,得通过Ajax的方式触发。

现在用户还没有点击按钮呢,你就发起请求了。

这边服务器也收到了响应的请求:

与此同时,浏览器也收到了响应 :

通过抓包也可以清楚的看到:

 

当前我们已经把数据提交到服务器保存了,但是我们还需要能够把服务器的数据拿回到客户端页面并显示。

等会儿!我们刚刚都已经可以从页面上直接读到消息了,为啥还要从服务器拿数据?

  1. 持久性:页面上显示的数据通常是在浏览器内存中保存的,刷新页面或重启浏览器后,这些数据会丢失。通过从服务器获取数据,可以保持数据的持久性,确保用户数据不会丢失。

  2. 共享数据:如果多个客户端打开同一页面,它们可以共享服务器上的数据。这意味着多个用户可以查看相同的数据,而不是每个用户都有自己的本地副本。

所以我们就需要客户端在页面加载的时候就发起这个GET请求。

前端script标签里面放的代码都是页面加载的时候会执行的代码,所以我们把它放到最外层就好:

(1)客户端在页面加载的时候发起一个HTTP请求:

        // 直接在 script 里面写的 js 代码, 就是在页面加载时被执行到的. 
        // 发起一个 get 请求, 从服务器获取到数据
        // get 请求不需要 body, 也就不需要上述 data 和 contentType 属性了. 
        $.ajax({
            type: 'get', 
            url: 'message',
            success: function(body) {
            // 处理服务器返回的响应数据. (json 格式的数组了)
             }
        });

(2) 服务器收到这个请求,要处理这个请求并生成响应。

我们需要写一个对应的doGet方法:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理当前获取消息列表的 get 请求. 不需要解析参数, 直接返回数据即可.
        resp.setStatus(200);
        resp.setContentType("application/json; charset=utf8");
        String respJson =;
        resp.getWriter().write(respJson);
    }

这只是我们的直观想法,但是现实中的情况比我们设想的简单多了!

Java里面的Jackson本身就支持把List类型的数据转成json数组。

Jackson看到了MessageList是一个 List<Message> 类型的数据,Jackson 库会自动将其转换为 JSON 数组,其中每个 Message 对象都会被转换为一个 JSON 对象。所以Jackson自动便遍历List里面的每一个元素,把每个元素分别转成 json字符串。

于是乎,这几行代码就搞定了这件事情。

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理当前获取消息列表的 get 请求. 不需要解析参数, 直接返回数据即可.
        resp.setStatus(200);
        resp.setContentType("application/json; charset=utf8");
        String respJson =objectMapper.writeValueAsString(messageList);
        resp.getWriter().write(respJson);
    }

但是!一定要确保这几个代码的执行顺序!!!

setStatus和setContentType必须在getWriter前面,否则就不可能生效,还会构造出一个非法的http响应报文。

这个事情可以认为是Servlet的bug:

setStatus 和 setContentType 这两个方法应该在调用 getWriter() 之前设置,以确保正确设置 HTTP 响应的状态码和内容类型。这是因为一旦 getWriter() 被调用,响应头信息就已经被发送给客户端,此时再调用 setStatus 或 setContentType 将无效。

这是 Servlet 规范的要求,以确保生成正确的 HTTP 响应报文。所以,确保以下顺序是正确的:

response.setStatus(HttpServletResponse.SC_OK); // 设置状态码,例如 200 OK
response.setContentType("text/html"); // 设置内容类型,例如 HTML
PrintWriter out = response.getWriter(); // 获取输出流
// 在这之后,将数据写入输出流

如果你不按正确的顺序执行这些方法,可能会导致生成非法的 HTTP 响应,从而引发问题或错误。这是 Servlet 编程中的一个常见陷阱,需要谨慎处理。确保遵循 Servlet 规范以生成有效的 HTTP 响应。 

(3)客户端收到响应,就需要针对响应数据进行解析处理,把响应中的信息构造成页面元素(HTML片段)并显示出来。

 这个代码是整个“表白墙 ”程序最复杂的代码,我们需要拼接出HTML片段。

此处的 body 就是服务器返回的响应,数据中 JSON 格式的数组。

当响应中,header 带有 Content-Type: application/json,jQuery 就会自动的把 JSON 字符串解析成 JavaScript 对象了。

如果没有带 Content-Type: application/json,就需要通过 JavaScript 代码 JSON.parse 方法来手动将 JSON 字符串转成 JavaScript 对象。

很明显,我们Java代码中是有这行代码的:

所以此时 jQuery 就会自动的把 JSON 字符串解析好,当下的body就已经是一个JS数组了。

            success: function(body) {
                // 由于响应中已经有 Content-Type: application/json 了, 就不需要使用 parse 方法手动转换了.  
                // body = JSON.parse(body);

                // 处理服务器返回的响应数据. (json 格式的数组了)
                for (let i = 0; i < body.length; i++) {
                    // body 是一个数组, 此时 message 也就是 js 对象了. 
                    // 这个 message 对象里, 有三个属性, from, to, message
                    let message = body[i];

                    // 根据 message 对象构建 html 片段, 把这个片段给显示到网页上. 
                }
            }

    success: function(body) {
                // 由于响应中已经有 Content-Type: application/json 了, 就不需要使用 parse 方法手动转换了.  
                // body = JSON.parse(body);

                // 拿到 container 这个元素
                let containerDiv = document.querySelector('.container');
               
                // 处理服务器返回的响应数据. (json 格式的数组了)
                for (let i = 0; i < body.length; i++) {
                    // body 是一个数组, 此时 message 也就是 js 对象了. 
                    // 这个 message 对象里, 有三个属性, from, to, message
                    let message = body[i];

                    // 根据 message 对象构建 html 片段, 把这个片段给显示到网页上. 
                    // createElement 方法就能构造出一个 html 标签. 
                    // 此时就得到了 <div></div> 
                    let div = document.createElement('div');
                    // 还需要往里面设置一个 属性 , class="row" (设置这个属性, 是为了让 css 能够给这个元素设置一些样式)
                    // 此时就得到了 <div class="row"></div>
                    div.className = 'row';
                    // 给这个 div 里设置内容
                    // 此时就得到了 <div class="row">张三 对 李四 说: 我喜欢你很久了</div>
                    div.innerHTML = message.from + " 对 " + message.to + " 说: " + message.message;
                    // 把 div 添加到 containerDiv 的末尾
                    containerDiv.appendChild(div);
                }
            }

HTML里面的每个元素/标签都可以用一个JS对象来表示,反之JS的对象也可以设置到页面中。这就是DOM(Document Object Model)文档对象模型。 

DOM(Document Object Model)是一种用来表示和操作HTML文档的编程接口。在DOM中,HTML文档中的每个元素(标签)都被表示为JavaScript对象,这使得开发人员可以使用JavaScript来访问、修改和操作HTML文档的结构和内容。

DOM允许开发人员:

  1. 访问文档中的所有元素和其属性。
  2. 修改文档的结构,例如添加、删除或移动元素。
  3. 更改元素的内容、样式和属性。
  4. 响应用户的交互事件,如单击、键盘输入等。

DOM 的树状结构表示了HTML文档的层次结构,其中顶层的元素是文档元素 <html>,然后包含了 <head> 和 <body> 元素,以及其他嵌套的元素。每个元素都是DOM树中的一个节点,可以使用JavaScript来访问和操作这些节点。

<!DOCTYPE html>
<html>
  <head>
    <title>DOM Example</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>This is a paragraph.</p>
  </body>
</html>

这样总的前端代码就写好了: 

    <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
            // 4. 把用户填写的内容, 发送给服务器. 让服务器来保存. 
            //    $ 是 jquery 提供的全局变量. ajax 就是 $ 的一个方法. 
            //    ajax 的参数是一个 js 对象, 可以有很多属性
            let requestbody = {
                "from": from,   // from 变量里的值, 就是第一个输入框的内容, "张三"
                "to": to,       // to 变量的值, 就是第二个输入框的内容, "李四"
                "message": msg  // msg 变量的值, 就是第三个输入框的内容, "我喜欢你很久了"
            };
            // 上述 body 是一个 js 对象, 还需要转成 json 字符串. 
            let jsonString = JSON.stringify(requestbody);
            $.ajax({
                type: 'post',
                url: 'message',
                contentType: 'application/json; charset=utf8',
                data: jsonString,
                success: function(responsebody) {
                     // 这个回调就是收到响应之后要执行的代码了. 
                    // 前端使用 console.log 打印日志到控制台. (chrome 开发者工具的控制台)
                    console.log("responseBody: " + responseBody);
                }
            });
        }

        // 直接在 script 里面写的 js 代码, 就是在页面加载时被执行到的. 
        // 发起一个 get 请求, 从服务器获取到数据
        // get 请求不需要 body, 也就不需要上述 data 和 contentType 属性了. 
        $.ajax({
            type: 'get', 
            url: 'message',
            success: function(body) {
                // 由于响应中已经有 Content-Type: application/json 了, 就不需要使用 parse 方法手动转换了.  
                // body = JSON.parse(body);

                // 拿到 container 这个元素
                let containerDiv = document.querySelector('.container');
                // 处理服务器返回的响应数据. (json 格式的数组了)
                for (let i = 0; i < body.length; i++) {
                    // body 是一个数组, 此时 message 也就是 js 对象了. 
                    // 这个 message 对象里, 有三个属性, from, to, message
                    let message = body[i];

                    // 根据 message 对象构建 html 片段, 把这个片段给显示到网页上. 
                    // createElement 方法就能构造出一个 html 标签. 
                    // 此时就得到了 <div></div> 
                    let div = document.createElement('div');
                    // 还需要往里面设置一个 属性 , class="row" (设置这个属性, 是为了让 css 能够给这个元素设置一些样式)
                    // 此时就得到了 <div class="row"></div>
                    div.className = 'row';
                    // 给这个 div 里设置内容
                    // 此时就得到了 <div class="row">张三 对 李四 说: 我喜欢你很久了</div>
                    div.innerHTML = message.from + " 对 " + message.to + " 说: " + message.message;
                    // 把 div 添加到 containerDiv 的末尾
                    containerDiv.appendChild(div);
                }
            }
        });
        //直接在script里面写的JS代码,就是在页面加载时被执行到的
        //发起一个get请求,从服务器获取到数据
        //get请求不需要body
    </script>

现在我们还是再来看看效果: 

提交后再次刷新页面会发现,数据还保留:

甚至我们单开一个标签页,数据仍然存在:

 

 但是,我们目前只是把数据保存在内存中,一旦服务器重启,数据就消失了。

所以我们接下来就来看看如何把消息数据存储到数据库中,并且把数据库引入到代码中。

数据存入数据库

使用文件的方式存储留言固然可行, 但是     ,我们还可以借助数据库完成存储工作。

1、在 pom.xml 中引入 mysql 的依赖:

 

2、建库建表:

建库建表需要用到SQL,都可以写到文件中,后续如果需要把表啥的往其他机器上迁移,建表操作就会比较方便。

注意:我们在上下文中使用了 "from"、'to' 作为属性名或变量名,且它与JavaScript的保留关键字冲突,需要使用反引号(backticks)将它作为属性名,这样可以确保使用关键字 "from" 、"to"作为属性名不会导致语法错误。

create database if not exists message_wall charset utf8;

use message_wall;

-- 删表目的是为了, 防止之前数据库里有一样的表, 对咱们的代码产生干扰.
drop table if exists message;
create table message (`from` varchar(1024), `to` varchar(1024), message varchar(1024));

然后我们打开MySQL数据库复制粘贴:

 

3、编写数据库代码:

使用JDBC编写代码:

首先,既然我们引入了数据库,那么:

就可以删除了。

先创建数据源:

    private DataSource dataSource = new MysqlDataSource();

    @Override
    public void init() throws ServletException {
        // 1. 创建数据源
        ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/message_wall?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("2222");
    }

写一个保存、插入数据的方法: 

    private void save(Message message) throws SQLException {
        // 通过这个方法把 message 插入到数据库中

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造 SQL
        String sql = "insert into message values(?, ?, ?)";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1, message.from);
        statement.setString(2, message.to);
        statement.setString(3, message.message);

        // 3. 执行 SQL
        statement.executeUpdate();

        // 4. 回收资源
        statement.close();
        connection.close();
    }

最后是一个从数据库中读取的方法: 

   private List<Message> load() throws SQLException {
        // 通过这个方法从数据库读取到 message.

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造 SQL
        String sql = "select * from message";
        PreparedStatement statement = connection.prepareStatement(sql);

        // 3. 执行 sql
        ResultSet resultSet = statement.executeQuery();

        // 4. 遍历结果集合
        List<Message> messageList = new ArrayList<>();
        while (resultSet.next()) {
            Message message = new Message();
            message.from = resultSet.getString("from");
            message.to = resultSet.getString("to");
            message.message = resultSet.getString("message");
            messageList.add(message);
        }

        // 5. 回收资源
        resultSet.close();
        statement.close();
        connection.close();

        // 6. 返回 messageList
        return messageList;
    }

 我们总代码就在这里:

package org.example;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();

    // 引入数据库, 此时 messageList 就不再需要了.
    // private List<Message> messageList = new ArrayList<>();

    private DataSource dataSource = new MysqlDataSource();

    @Override
    public void init() throws ServletException {
        // 1. 创建数据源
        ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/message_wall?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("2222");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 读取前端发来的数据, 把这个数据保存到服务器这边.
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        System.out.println("请求中收到的 message: " + message);
        // 最简单的办法, 直接在内存中保存. 可以使用一个集合类, 把所有收到的 message 都存到内存中.
        // 很明显, 保存到内存, 并非是一个非常合理的办法. 后续一旦重启服务器, 数据丢失了.
        // 相比之下, 把这个数据持久化存储到数据库中, 更科学的.
        // messageList.add(message);
        // 插入数据库
        try {
            save(message);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        // 返回一个响应
        resp.setStatus(200);
        resp.getWriter().write("ok");
        // resp.setContentType("application/json");
        // resp.getWriter().write("{ ok: true }");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理当前获取消息列表的 get 请求. 不需要解析参数, 直接返回数据即可.
        resp.setStatus(200);
        resp.setContentType("application/json; charset=utf8");
        // 从数据库查询
        List<Message> messageList = null;
        try {
            messageList = load();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        String respJson = objectMapper.writeValueAsString(messageList);
        resp.getWriter().write(respJson);
    }

    private void save(Message message) throws SQLException {
        // 通过这个方法把 message 插入到数据库中

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造 SQL
        String sql = "insert into message values(?, ?, ?)";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1, message.from);
        statement.setString(2, message.to);
        statement.setString(3, message.message);

        // 3. 执行 SQL
        statement.executeUpdate();

        // 4. 回收资源
        statement.close();
        connection.close();
    }

    private List<Message> load() throws SQLException {
        // 通过这个方法从数据库读取到 message.

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造 SQL
        String sql = "select * from message";
        PreparedStatement statement = connection.prepareStatement(sql);

        // 3. 执行 sql
        ResultSet resultSet = statement.executeQuery();

        // 4. 遍历结果集合
        List<Message> messageList = new ArrayList<>();
        while (resultSet.next()) {
            Message message = new Message();
            message.from = resultSet.getString("from");
            message.to = resultSet.getString("to");
            message.message = resultSet.getString("message");
            messageList.add(message);
        }

        // 5. 回收资源
        resultSet.close();
        statement.close();
        connection.close();

        // 6. 返回 messageList
        return messageList;
    }
}

现在让我们来重启服务器看看效果: 

现在随着我们刷新网页,刚刚写进去的信息就显示出来了。

我们也可以重新提交一条信息: 

 

 与此同时数据库里也有了新的信息:

如此这般,我们的表白墙就大功告成了! 

我们已经完成了 "表白墙" 程序的前端和后端开发,数据也能够保存在MySQL数据库中,但其他客户端仍然无法访问,我们需要执行以下步骤来使我们的应用对外可访问:

  1. 部署后端服务:确保我们的后端服务(Java应用)已经部署到一个可访问的服务器上。这可以是我们自己的服务器,也可以使用云服务提供商(如AWS、Heroku、Google Cloud等)提供的云服务器。

  2. 配置服务器防火墙和网络:确保服务器上的防火墙设置允许来自外部客户端的HTTP请求访问我们的后端服务。我们需要配置服务器的网络设置,以便客户端可以通过公共IP地址或域名访问服务器。

  3. 域名和DNS设置:如果我们有一个自定义域名,确保将域名与服务器的IP地址关联起来。如果没有域名,我们可以使用服务器的公共IP地址来访问应用。如果使用域名,还需要在DNS设置中创建相应的记录,以确保域名解析到正确的IP地址。

  4. Web服务器配置:如果我们使用的是Java Web应用,通常会使用Web服务器(如Tomcat)来托管我们的应用。确保Web服务器已正确配置,以便处理HTTP请求,并将它们路由到我们的Java应用。

  5. HTTPS加密:对于任何涉及用户数据或敏感信息的应用,强烈建议启用HTTPS加密以保护通信安全。我们可以获得SSL/TLS证书并配置服务器以支持HTTPS。

  6. 防止安全漏洞:确保我们的应用程序没有常见的安全漏洞,如SQL注入、跨站脚本(XSS)、跨站请求伪造(CSRF)等。定期进行安全审计和漏洞扫描,以确保应用的安全性。

  7. 监控和维护:建立监控和日志记录机制,以便随时检测应用问题。定期维护服务器和应用程序,确保它们保持最新版本并正常运行。

  8. 测试访问:确保我们可以从其他客户端设备(例如移动设备或不同浏览器)访问我们的应用,并进行充分的测试以确保一切正常。

  9. 文档和用户支持:提供文档和用户支持,以帮助其他人了解如何使用我们的应用。

一旦我们完成了上述步骤,我们的 "表白墙" 应用就应该可以从其他客户端设备访问了。确保维护和监控我们的应用,以确保它始终可用和安全。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/120591.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis 线程、持久化和监控

Redis 线程、持久化和监控 Redis线程模型 Redis主线程模型 图1 Redis 6.0之前的主线程模型 IO多路复用程序指的是单个线程监听多个套接字连接&#xff08;Socket&#xff09;&#xff0c;当IO多路复用程序将多个Socket上的就绪事件放置于队列中&#xff0c; Redis主线程一次处…

App备案-iOS云管理式证书 Distribution Managed 公钥及证书SHA-1指纹的获取方法

​ 根据近日工业和信息化部发布的《工业和信息化部关于开展移动互联网应用程序备案工作的通知》&#xff0c;相信不少要进行IOS平台App备案的朋友遇到了一个问题&#xff0c;就是apple不提供云管理式证书的下载&#xff0c;也就无法获取公钥及证书SHA-1指纹。 ​ 已经上架的应用…

1.UML面向对象类图和关系

文章目录 4种静态结构图类图类的表示类与类之间的关系依赖关系(Dependency)关联关系(Association)聚合(Aggregation)组合(Composition)实现(Realization)继承/泛化(Inheritance/Generalization)常用的UML工具reference欢迎访问个人网络日志🌹🌹知行空间🌹🌹 4种静态结构…

SHCTF-校外赛道

SHCTF-校外赛道 [WEEK1]babyRCE 1 (1)more:一页一页的显示档案内容2 (2)less:与 more 类似&#xff0c;但是比 more 更好的是&#xff0c;他可以[pg dn][pg up]翻页3 (3)head:查看头几行4 (4)tac:从最后一行开始显示&#xff0c;可以看出 tac 是 cat 的反向显示5 (5)tail:查看…

建链时,please install openssl! use “openssl version“ command to check.

please install openssl! use “openssl version” command to check. 但是我已经安装了 编辑build_chain.sh文件 也可以用vi或者gedit命令 将 [ ! -z “ ( o p e n s s l v e r s i o n ∣ g r e p 1.0.2 ) " ] ∣ ∣ [ ! − z " (openssl version | grep 1.0.2)…

Maven中的继承与聚合

一&#xff0c;继承 前面我们将项目拆分成各个小模块&#xff0c;但是每个小模块中有很多相同的依赖于是我们创建一个父工程将模块中相同的依赖定义在父工程中&#xff0c;然后子工程继承父工程Maven作用&#xff1a;简化依赖配置&#xff0c;统一依赖管理,可以实现多重继承像J…

Halcon WPF 开发学习笔记(0):开篇介绍

文章目录 文章专栏Halcon是什么&#xff1f;安装教学视频链接简单来说 Halcon快速开发环境确认新建项目 文章专栏 Halcon开发 Halcon是什么&#xff1f; 史上最全VisionPro和Halcon 的详细对比 Halcon简述 Halcon基础大全&#xff08;基础算子、高阶算子、数组、分割、字符检测…

基于C#的GRPC

GRPC gRPC&#xff08;gRPC Remote Procedure Call&#xff09;是由Google开发的高性能、跨语言的远程过程调用框架。它基于HTTP/2协议进行通信&#xff0c;支持多种编程语言&#xff0c;包括C, C#, Java, Python等&#xff0c;使不同语言的应用程序可以通过远程调用相互通信。…

Redis系列之常见数据类型应用场景

文章目录 String简单介绍常见命令应用场景 Hash简单介绍常见命令应用场景 List简单介绍常见命令应用场景 Set简单介绍常见命令应用场景 Sorted Set(Zset)简单介绍常见命令应用场景 Bitmap简单介绍常见命令应用场景 附录 Redis支持多种数据类型&#xff0c;比如String、hash、li…

Nat. Med. | 基于遗传学原发部位未知癌症的分类和治疗反应预测

今天为大家介绍的是来自Alexander Gusev团队的一篇论文。原发部位未知癌症&#xff08;Cancer of unknown primary&#xff0c;CUP&#xff09;是一种无法追溯到其原发部位的癌症&#xff0c;占所有癌症的3-5&#xff05;。CUP缺乏已建立的靶向治疗方法&#xff0c;导致普遍预后…

[java进阶]——方法引用改写Lambda表达式

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 &#x1f4d5;概念介绍&#xff1a; ⭐方法引用的前提条件&#xff1a; 1.引用静态方法 2.引用构造方法 ①类的构造&#xff1a; ②数组的构造&#xff1a; 3.引用本类或父类的成员方法 ①本类&#xff1…

三大基础排序 -选择排序、冒泡排序、插入排序

排序算法 文章目录 冒泡排序算法步骤动图代码优化总结 选择排序算法步骤动图代码总结 插入排序算法步骤动图代码总结 排序算法&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。一般默认排序是按照由小到大即…

Windows下MSYS2下载与安装

下载地址&#xff1a; 官网下载地址 https://www.msys2.org/阿里云镜像下载 https://mirrors.aliyun.com/msys2/distrib/x86_64/https://mirrors.aliyun.com/msys2/distrib/x86_64/msys2-x86_64-20231026.exe?spma2c6h.25603864.0.0.12b92551XW5OSM官网下载 ![官网下载](htt…

uniapp u-tabs表单如何默认选中

首先先了解该组件&#xff1b;该组件&#xff0c;是一个tabs标签组件&#xff0c;在标签多的时候&#xff0c;可以配置为左右滑动&#xff0c;标签少的时候&#xff0c;可以禁止滑动。 该组件的一个特点是配置为滚动模式时&#xff0c;激活的tab会自动移动到组件的中间位置。 …

算法竞赛——数论(一),数论内容的介绍,基础数论

文章目录 一&#xff0c; 数论学习路线的介绍和相关建议1&#xff0c;建议学习人群 &#xff1a;2&#xff0c;建议学习时长3&#xff0c;学习路线的介绍1&#xff0c;基础数论2&#xff0c;组合数学3&#xff0c;计算几何 二&#xff0c;基础数论第一部分 —— 快速幂和快速幂…

Java算法(三): 判断两个数组是否为相等 → (要求:长度、顺序、元素)相等

Java算法&#xff08;三&#xff09; 需求&#xff1a; 1. 定义一个方法&#xff0c;用于比较两个数组是否相同2. 需求&#xff1a;长度&#xff0c;内容&#xff0c;顺序完全相同package com.liujintao.compare;public class SameArray {public static void main (String[] a…

一篇文章让你Docker从入门到精通

一篇文章让你Docker从入门到精通 Docker简介docker的3要素docker安装--centos7示例docker底层原理docker常用命令docker镜像原理数据共享--容器数据卷数据卷容器dockerfile解析Dockerfile实战一 使用dockerfile构建ubuntu镜像&#xff0c;并在里面安装vim及网络工具 一张图展示…

论文阅读:Ensemble Knowledge Transfer for Semantic Segmentation

论文地址&#xff1a;https://ieeexplore.ieee.org/document/8354272 项目及数据地址&#xff1a;https://github.com/ishann/aeroscapes 发表时间&#xff1a;2018年5月7日 语义分割网络通常以严格监督的方式学习&#xff0c;即它们在相似的数据分布上进行训练和测试。在域转…

EPLAN软件中的术语-主数据‘’技术分享

在EPLAN中&#xff0c;主数据(Master Data)这个词被经常、反复地提及&#xff0c;我曾经困惑了很长时间&#xff0c;不得要领。在EPLAN的帮助系统中&#xff0c;它列举了一部分内容&#xff0c;说这些这些就是主数据&#xff0c;但没有解释什么是主数据&#xff0c;除了上面这些…

怎么压缩jpg大小?快来收藏这款jpg压缩工具

不管是工作还是日常生活中&#xff0c;都免不了要用到许多图片&#xff0c;其中jpg图片格式是最常见的一种格式&#xff0c;那么小伙伴们知道怎么将jpg格式压缩大小吗&#xff1f;jpg压缩不仅可以为我们的设备节省空间&#xff0c;还能避免许多对图片大小有限制的平台&#xff…