实现服务器版本---表白墙(Servlet)

目录

一、创建Servlet项目

二、约定前后端交互接口

三、前端代码

四、后端代码

五、效果演示


        结合Servlet API ,实现一个服务器版本表白墙。实现的这个表白墙,就通过服务器来保存这里的消息数据,进而做到 “持久化” 存储。

一、创建Servlet项目

1、目录结构:

2、web.xml

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
</web-app>

3、pom.xml 

需要添加的依赖:

    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.6.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>
    </dependencies>

 完整的 pom.xml :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>2023_4_30_Servlet</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.6.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>
    </dependencies>
</project>

二、约定前后端交互接口

        对于表白墙来说,主要提供两个接口:

  1. 告诉服务器,当前留言了一条什么样的数据
  2. 从服务器中获取到,当前都有什么留言数据

1、告诉服务器,当前留言了一条什么样的数据

        当用户点击提交按钮时,就会给服务器发送一个 HTTP 请求,让服务器把这个消息给存下来。

        约定好客户端发送的是什么样的 HTTP 请求,服务器返回一个什么样的请求。

请求:

POST/message

{

        from:" 黑猫 ",

        to:" 白猫 ",

        message:" 喵 "

}

(这里的请求格式是可以改变的) 

响应:

HTTP/1.1 200 OK

{

        ok:true

2、从服务器中获取到,当前都有什么留言数据

        当页面加载,就需要从服务器获取到曾经存储的消息内容,并显示。

请求:

GET/message

响应:

HTTP/1.1 200 OK

Content-Type:application/json

[

        {

                from:" 黑猫 ",

                to:" 白猫 ",

                message:" 喵 "

        } ,

        {

                from:" 猫 ",

                to:" gou猫 ",

                message:" 喵 "

        } ...

]

三、前端代码

        在前端代码实现页面的基础上,进行添加。

        因为要发送 POST 请求,所以先引入 jquery,以便使用 jquery ajax 来构造POST 请求。

    <!-- 引入jquery,导入 jquery 库的连接,以便后面能使用 jquery ajax -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>

加入 ajax 的代码,此处要加入的逻辑有两个部分

  1. 点击按钮提交的时候,ajax 要构造数据发送给服务器
  2. 页面加载的时候,从服务器获取消息列表,并在界面上直接显示 

构造POST请求:

            // 4.把当前获取到的输入框的内容,构造成一个 HTTP POST 请求,通过 ajax 发送给服务器
            let body = {
                from: from,
                to: to,
                message: msg
            };
            $.ajax({
                type: "post",
                url: "message",
                contentType:"application/json; charset=utf8",
                // 把 body 转成 json 字符串格式的对象
                data: JSON.stringify(body),
                success: function(body){
                    alert("消息提交成功!");
                },
                error: function(){
                    alert("消息提交失败!");
                }
            });

 注意:

在 JSON 中,key 是要带上引号的

在 JS 中,对象这里的 key 可以带引号,也可以不带引号。(正常是要带的,但是JS为了写起来方便,允许这里省略引号)但是如果 key 中有一些特殊符号,比如空格、- 这种,就必须带上引号。

let body = {

        from: from,
        to: to,
        message: msg
}; 

 返回曾经消息:

        function getMessage() {
            $.ajax({
                type: 'get',
                url: 'message',
                success: function(body) {
                    // 当前 body 已经是一个 js 对象数组了,ajax 会根据响应的 content type 来自动进行解析,
                    // 如果服务器返回的 content-type 已经是 application/json 了,ajax 就会把 body 自动转成 js 对象
                    // 如果客户端没有自动转,也可以通过 JSON.parse() 这个函数来手动转换

                    // 依次来取数组中的每个元素
                    let container = document.querySelector('.container');
                    for (let message of body) {
                        let div = document.createElement('div');
                        div.innerHTML = message.from + '对' + message.to + '说: ' + message.message;
                        div.className = 'row';
                        container.appendChild(div);
                    }
                }
            })
        }
        // 加上函数调用
        getMessage();

 对象 和 JSON 字符串之间的转换:

Java :

objectMapper.readValue :把 json 字符串转成 对象

objectMapper.writeValueAsString :把 对象 转成 json 字符串

JS :

JSON.parse :把 json 字符串 转成 对象

JSON.stringify :把 对象 转成 json 字符串

整体的前端代码:

<!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>
</head>
<body>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 100%;
        }

        h3 {
            text-align: center;
            padding: 30px 0;
            font-size: 24px;
        }

        p {
            text-align: center;
            color: #999;
            padding: 10px 0;
        }

        .row {
            width: 400px;
            height: 50px;
            margin: 0 auto;

            display: flex;
            justify-content: center;
            align-items: center;
        }

        .row span {
            width: 60px;
            font-size: 20px;
        }

        .row input {
            width: 300px;
            height: 40px;
            line-height: 40px;
            font-size: 20px;
            text-indent: 0.5em;
            /* 去掉输入框的轮廓线 */
            outline: none;
        }

        .row #submit {
            width: 300px;
            height: 40px;
            font-size: 20px;
            line-height: 40px;
            margin: 0 auto;

            color: white;
            background-color: orange;
            /* 去掉边框 */
            border: none;

            border-radius: 10px;
        }

        .row #submit:active {
            background-color: gray;
        }
    </style>
    <div class="container">
        <h3>表白墙</h3>
        <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>

    <!-- 引入jquery,导入 jquery 库的连接,以便后面能使用 jquery ajax -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>

    <script>
        // 加入 ajax 的代码,此处要加入的逻辑有两个部分
        // 点击按钮提交的时候,ajax 要构造数据发送给服务器
        // 页面加载的时候,从服务器获取消息列表,并在界面上直接显示


        function getMessage() {
            $.ajax({
                type: 'get',
                url: 'message',
                success: function(body) {
                    // 当前 body 已经是一个 js 对象数组了,ajax 会根据响应的 content type 来自动进行解析,
                    // 如果服务器返回的 content-type 已经是 application/json 了,ajax 就会把 body 自动转成 js 对象
                    // 如果客户端没有自动转,也可以通过 JSON.parse() 这个函数来手动转换

                    // 依次来取数组中的每个元素
                    let container = document.querySelector('.container');
                    for (let message of body) {
                        let div = document.createElement('div');
                        div.innerHTML = message.from + '对' + message.to + '说: ' + message.message;
                        div.className = 'row';
                        container.appendChild(div);
                    }
                }
            })
        }
        // 加上函数调用
        getMessage();


        // 当用户点击 submit, 就会获取到 input 中的内容, 从而把内容构造成一个 div, 插入到页面末尾. 
        let submitBtn = document.querySelector('#submit');
        submitBtn.onclick = function() {
            // 1. 获取到 3 个 input 中的内容. 
            let inputs = document.querySelectorAll('input');
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                // 用户还没填写完, 暂时先不提交数据. 
                return;
            }
            // 2. 生成一个新的 div, 内容就是 input 里的内容. 把这个新的 div 加到页面中. 
            let div = document.createElement('div');
            div.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            div.className = 'row';
            let container = document.querySelector('.container');
            container.appendChild(div);
            // 3. 清空之前输入框的内容. 
            for (let i = 0; i < inputs.length; i++) {
                inputs[i].value = '';
            }


            // 4.把当前获取到的输入框的内容,构造成一个 HTTP POST 请求,通过 ajax 发送给服务器
            let body = {
                from: from,
                to: to,
                message: msg
            };
            $.ajax({
                type: "post",
                url: "message",
                contentType:"application/json; charset=utf8",
                // 把body 转成字符串格式的对象
                data: JSON.stringify(body),
                success: function(body){
                    alert("消息提交成功!");
                },
                error: function(){
                    alert("消息提交失败!");
                }
            });
        }
    </script>
</body>
</html>

四、后端代码

        要实现 “ 持久化 ” 保存数据,则将数据都放在数据库中。

1、创建数据库

        创建数据库 MessageWall,创建表 messages。表的属性有 from、to、message。

2、实现 DButil 类

        注意这里使用了单例模式中的懒汉模式

  • ① 正确的位置加锁
  • ② volatile
  • ③ 双重 if 判定
public class DBUtil {
    //创建 MysqlDataSource 实例,设置 URL,USERNAME,PASSWORD 等属性
    //提供 getConnection 方法,和 MySQL 服务器建立连接
    //提供 close 方法,用来释放必要的资源
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/MessageWall?characterEncoding=utf8&useSSL=false";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "12345";

    private volatile static DataSource dataSource = null;

    private static DataSource getDataSource() {
        if (dataSource == null) {
            synchronized(DBUtil.class) {
                if (dataSource == null) {
                    dataSource = new MysqlDataSource();
                    ((MysqlDataSource)dataSource).setUrl(URL);
                    ((MysqlDataSource)dataSource).setUser(USERNAME);
                    ((MysqlDataSource)dataSource).setPassword(PASSWORD);
                }
            }
        }
        return dataSource;
    }

    public static Connection getConnection() throws SQLException {
        return getDataSource().getConnection();
    }

    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

注意:

URL 中,

jdbc:mysql://127.0.0.1:3306/MessageWall?characterEncoding=utf8&useSSL=false

标红位置是自己所建的数据库的名称。 

 3、实现 MessageServlet 类

首先创建类 Message :

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

创建 MessageServlet 类,继承自 HttpServlet,重写 doPost方法 以及 doGet 方法。(因为在约定接口时,接口一规定传来的请求时 POST 请求,接口儿规定传来的请求时 GET 请求)

doPost方法中:

        先将请求信息转换成 Message 对象,再将 Message 对象的内容存入数据库。然后设置了响应的内容类型为 json格式 和 字符集为utf-8 。然后再向 body 中写入约定的 POST响应 的内容。


    //通过这个关键类,去解析Json对象/构造Json对象
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //处理提交消息请求
        //req.getInputStream():先从流对象中读取到数据内容,
        //Message.class:把字符串(JSON)转成 Message 对象
        //把结果赋值到 message 对象里
        Message message = objectMapper.readValue(req.getInputStream(),Message.class);
        
        //存数据
        save(message);

        //要返回Json格式的Json数据
        //通过ContentType来告知页面说,当前返回的数据是Json格式
        //有了这样的声明,此时 jquery ajax 就会自动的帮我们把 json字符串 转成 js 对象
        //如果没有,jquery ajax 就只是当成 json字符串 来处理
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write("{\"ok\":true}");
    }

    //将数据存入数据库中
    private void save(Message message) {
        //把一条消息保存到数据库中
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1.和数据库建立连接
            connection = DBUtil.getConnection();
            //2.构造 SQL 语句进行插入
            String sql = "insert into messages values(?,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1,message.from);
            statement.setString(2,message.to);
            statement.setString(3,message.message);
            //3.执行 SQL
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //4.关闭
            //因为是插入语句,所以没有result
            DBUtil.close(connection,statement,null);
        }
    }

doGet方法中:

        先从数据库中取出之前存储的信息存到链表中,将 Message对象 转换成 json字符串。然后设置响应的内容格式是 json,设置字符集为 utf-8。然后写入作为GET方法响应的body中。

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取消息列表
        //把消息列表获取到,List中内容返回到响应里就可以了
        //只要把消息列表中的内容整个的都返回给客户端即可
        //此处需要使用 ObjectMapper 把 Java 对象,转成 JSON 格式字符串
        List<Message> messages = load();
        String jsonString = objectMapper.writeValueAsString(messages);
        System.out.println("jsonString: "+ jsonString);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(jsonString);
    }

    //从数据库中取出数据
    private List<Message> load() {
        //从数据库中获取到所有的消息
        List<Message> messages = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select * from messages";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while(resultSet.next()) {
                Message message = new Message();
                message.from = resultSet.getString("from");
                message.to = resultSet.getString("to");
                message.message = resultSet.getString("message");
                messages.add(message);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return messages;
    }

 整体代码:

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.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WangWZ
 * Date: 2023-04-30
 * Time: 18:46
 */

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

@WebServlet("/message")
//改用数据库存储,实现存和取两个方法---save 和 load
public class MessageServlet extends HttpServlet{
    //通过这个关键类,去解析Json对象/构造Json对象
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //处理提交消息请求
        //req.getInputStream():先从流对象中读取到数据内容,
        //Message.class:把字符串(JSON)转成这样的 Message 对象
        //把结果赋值到 message 对象里
        Message message = objectMapper.readValue(req.getInputStream(),Message.class);

        save(message);

        //通过ContentType来告知页面说,当前返回的数据是Json格式
        //有了这样的声明,此时 jquery ajax 就会自动的帮我们把字符串转成 js 对象
        //如果没有,jquery ajax 就只是当成字符串来处理
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write("{\"ok\":true}");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取消息列表
        //把消息列表获取到,List中内容返回到响应里就可以了
        //只要把消息列表中的内容整个的都返回给客户端即可
        //此处需要使用 ObjectMapper 把 Java 对象,转成 JSON 格式字符串
        List<Message> messages = load();
        String jsonString = objectMapper.writeValueAsString(messages);
        System.out.println("jsonString: "+ jsonString);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(jsonString);
    }

    //将数据存入数据库中
    private void save(Message message) {
        //把一条消息保存到数据库中
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //1.和数据库建立连接
            connection = DBUtil.getConnection();
            //2.构造 SQL 语句进行插入
            String sql = "insert into messages values(?,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1,message.from);
            statement.setString(2,message.to);
            statement.setString(3,message.message);
            //3.执行 SQL
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //4.关闭
            //因为是插入语句,所以没有result
            DBUtil.close(connection,statement,null);
        }
    }

    //从数据库中取出数据
    private List<Message> load() {
        //从数据库中获取到所有的消息
        List<Message> messages = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select * from messages";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while(resultSet.next()) {
                Message message = new Message();
                message.from = resultSet.getString("from");
                message.to = resultSet.getString("to");
                message.message = resultSet.getString("message");
                messages.add(message);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return messages;
    }
}

五、效果演示

  

 此时数据库:

无论页面刷新还是服务器重启,数据都不会消失。(“持久化”存储数据) 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值