实现第一个服务器版本的表白墙程序

骑猪看日落 2024-03-22 18:36 97阅读 0赞

文章目录

  • 表白墙
    • 前言
      1. 环境部署
      • 1.1 创建maven项目
      • 1.2 引入依赖
      • 1.3 创建目录结构
      • 1.4 部署程序
      1. 前端页面
      1. 后端实现
      • 3.1 后端逻辑
      • 3.2 后端代码

表白墙

前言

基于MySQL数据库和servlet实现的前后端交互的服务器版本表白墙。在页面输入表白内容,在本地通过数据库存储历次表白内容,保证网页刷新重进时数据仍存在。以下为打开表白墙效果展示:

在这里插入图片描述
我们可以在输入框输入表白对象,表白内容然后提交就能显示到新动态中,提交之后输入框自动清空内容。

完整项目代码可以通过我的gitee获取MessageWall

1. 环境部署

1.1 创建maven项目

我们在idea里面点击new project创建一个maven项目,然后一路next选择好项目位置以及命名即可。
在这里插入图片描述
在这里插入图片描述

1.2 引入依赖

在创建完maven项目之后我们需要加载依赖,部分同学可能由于网络问题难以加载,可以多试几遍,实在不行可以更换国内的镜像下载此处不作多介绍。以下需要引入的依赖主要有:java.servlet-api、mysql-connector-java,以及后续发送json格式的数据需要的jackson依赖(jackson-databind)

我们通过maven仓库官网https://mvnrepository.com/ 即可获取。

如图:复制链接黏贴到我们项目的pom.xml文件
在这里插入图片描述

代码 : 以下是我部署的依赖可以直接复制黏贴

  1. <dependencies>
  2. <!-- servlet依赖-->
  3. <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
  4. <dependency>
  5. <groupId>javax.servlet</groupId>
  6. <artifactId>javax.servlet-api</artifactId>
  7. <version>3.1.0</version>
  8. <scope>provided</scope>
  9. </dependency>
  10. <!-- jackson依赖-->
  11. <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
  12. <dependency>
  13. <groupId>com.fasterxml.jackson.core</groupId>
  14. <artifactId>jackson-databind</artifactId>
  15. <version>2.14.2</version>
  16. </dependency>
  17. <!-- 数据库依赖-->
  18. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  19. <dependency>
  20. <groupId>mysql</groupId>
  21. <artifactId>mysql-connector-java</artifactId>
  22. <version>5.1.47</version>
  23. </dependency>
  24. </dependencies>

1.3 创建目录结构

在我们的webapp目录下面创建一个WEB-INF包并创建一个web.xml,此处结构以及名字不能改动。
在这里插入图片描述
web.xml内容:

  1. <!DOCTYPE web-app PUBLIC
  2. "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  3. "http://java.sun.com/dtd/web-app_2_3.dtd" >
  4. <web-app>
  5. <display-name>Archetype Created Web Application</display-name>
  6. </web-app>

1.4 部署程序

我们在插件市场搜索 Smart Tomcat 插件,将Tomcat集成到idea就不用我们手动打包,之后点击即可运行。
在这里插入图片描述
然后点击新增运行配置,将Smart Tomcat加入,此处选择Tomcat路径,如果还没有下载Tomcat,可以通过官网下载。博主使用的是 3.1版本的servlet+tomcat 8

在这里插入图片描述
在这里插入图片描述

然后就可以点击运行:出现以下结果就是部署成功了。之后我们就可以通过127.0.0.1:8080/MessageWall/xx.html(html页面)访问,其中MessageWall是Context path,一般默认是项目名。

在这里插入图片描述

2. 前端页面

由于该程序只是作为我们学习使用servlet实现前后端交互的一个开端,我们我们前端页面只包括:输入框 input 、提交按钮 button 、无语义标签 div 用来划分页面。

页面展示:
在这里插入图片描述

HTML代码: 除了html本身代码还包含通过 style 标签引入的css代码,通过 script 标签引入的js代码。

通过css实现页面样式以及布局,通过js实现提交按钮的点击效果(点击变灰)以及向服务器发送GET请求并获取数据加载到页面。

注意: 本次我们采用的是通过 form表单构造json格式的get请求发送到服务器 ,需要通过 jquery的ajax 实现:

  1. 导入jquery cdn,在浏览器搜索即可,也可以直接下载我的

  2. 通过ajax构造请求

    $.ajax({

    1. type: 'get',
    2. url: 'message',
    3. success: function(body) {
    4. // 此处拿到的 body 就是一个 js 的对象数组了.
    5. // 本来服务器返回的是一个 json 格式的字符串, 但是 jquery 的 ajax 能够自动识别
    6. // 自动帮我们把 json 字符串转成 js 对象数组
    7. // 接下来遍历这个数组, 把元素取出来, 构造到页面中即可
    8. let containerDiv = document.querySelector('.qian')
    9. for (let message of body) {
    10. // 针对每个元素构造一个 div
    11. let rowDiv = document.createElement('div');
    12. rowDiv.className = 'row message';
    13. rowDiv.innerHTML = message.from + ' 对 ' + message.to + ' 说: ' + message.message;
    14. containerDiv.appendChild(rowDiv);
    15. }
    16. }
    17. });

完整代码:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>表白墙</title>
  8. <style>
  9. /* * 通配符选择器, 是选中页面所有元素 */
  10. * {
  11. /* 消除浏览器的默认样式. */
  12. margin: 0;
  13. padding: 0;
  14. box-sizing: border-box;
  15. }
  16. .container {
  17. width: 600px;
  18. margin: 20px auto;
  19. }
  20. h1 {
  21. text-align: center;
  22. }
  23. p {
  24. text-align: center;
  25. color: #666;
  26. margin: 20px 0;
  27. }
  28. .row {
  29. /* 开启弹性布局 */
  30. display: flex;
  31. height: 40px;
  32. /* 水平方向居中 */
  33. justify-content: center;
  34. /* 垂直方向居中 */
  35. align-items: center;
  36. }
  37. .row span {
  38. width: 80px;
  39. }
  40. .row input {
  41. width: 200px;
  42. height: 30px;
  43. }
  44. .row button {
  45. width: 280px;
  46. height: 30px;
  47. color: white;
  48. background-color: orange;
  49. /* 去掉边框 */
  50. border: none;
  51. border-radius: 5px;
  52. }
  53. /* 点击的时候有个反馈 */
  54. .row button:active {
  55. background-color: grey;
  56. }
  57. .qian{
  58. width: 300px;
  59. height: 500px;
  60. margin: 20px auto;
  61. background-color: pink;
  62. text-align: center;
  63. }
  64. </style>
  65. </head>
  66. <body>
  67. <div class="container">
  68. <h1>表白墙</h1>
  69. <p>输入内容后点击提交, 信息会显示到下方表格中</p>
  70. <div class="row">
  71. <span>谁: </span>
  72. <input type="text">
  73. </div>
  74. <div class="row">
  75. <span>对谁: </span>
  76. <input type="text">
  77. </div>
  78. <div class="row">
  79. <span>说: </span>
  80. <input type="text">
  81. </div>
  82. <div class="row">
  83. <button id="submit">提交</button>
  84. </div>
  85. <div class="row">
  86. <button id="revert">撤销</button>
  87. </div>
  88. <!-- 公示栏-->
  89. <div class="qian">
  90. <h2>新动态</h2>
  91. </div>
  92. </div>
  93. <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
  94. <script>
  95. // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示.
  96. // 点击的时候, 获取到三个输入框中的文本内容
  97. // 创建一个新的 div.row 把内容构造到这个 div 中即可.
  98. let containerDiv = document.querySelector('.qian');
  99. let inputs = document.querySelectorAll('input');
  100. let button = document.querySelector('#submit');
  101. button.onclick = function() {
  102. // 1. 获取到三个输入框的内容
  103. let from = inputs[0].value;
  104. let to = inputs[1].value;
  105. let msg = inputs[2].value;
  106. if (from == '' || to == '' || msg == '') {
  107. return;
  108. }
  109. // 2. 构造新 div
  110. let rowDiv = document.createElement('div');
  111. rowDiv.className = 'row message';
  112. rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
  113. containerDiv.appendChild(rowDiv);
  114. // 3. 清空之前的输入框内容
  115. for (let input of inputs) {
  116. input.value = '';
  117. }
  118. // 4. [新增] 给服务器发起 post 请求, 把上述数据提交到服务器这边
  119. let body = {
  120. "from": from,
  121. "to": to,
  122. "message": msg
  123. };
  124. let strBody = JSON.stringify(body);
  125. console.log("strBody: " + strBody);
  126. $.ajax({
  127. type: 'post',
  128. url: 'message',
  129. data: strBody,
  130. contentType: "application/json; charset=utf8",
  131. success: function(body) {
  132. console.log("数据发布成功");
  133. }
  134. });
  135. }
  136. let revertButton = document.querySelector('#revert');
  137. revertButton.onclick = function() {
  138. // 删除最后一条消息.
  139. // 选中所有的 row, 找出最后一个 row, 然后进行删除
  140. let rows = document.querySelectorAll('.message');
  141. if (rows == null || rows.length == 0) {
  142. return;
  143. }
  144. containerDiv.removeChild(rows[rows.length - 1]);
  145. }
  146. // [新增] 在页面加载的时候, 发送 GET 请求, 从服务器获取到数据并添加到页面中
  147. $.ajax({
  148. type: 'get',
  149. url: 'message',
  150. success: function(body) {
  151. // 此处拿到的 body 就是一个 js 的对象数组了.
  152. // 本来服务器返回的是一个 json 格式的字符串, 但是 jquery 的 ajax 能够自动识别
  153. // 自动帮我们把 json 字符串转成 js 对象数组
  154. // 接下来遍历这个数组, 把元素取出来, 构造到页面中即可
  155. let containerDiv = document.querySelector('.qian')
  156. for (let message of body) {
  157. // 针对每个元素构造一个 div
  158. let rowDiv = document.createElement('div');
  159. rowDiv.className = 'row message';
  160. rowDiv.innerHTML = message.from + ' 对 ' + message.to + ' 说: ' + message.message;
  161. containerDiv.appendChild(rowDiv);
  162. }
  163. }
  164. });
  165. </script>
  166. </body>
  167. </html>

3. 后端实现

在后端实现中创建两个类即可,MessageServlet类由于实现前后端交互,DBUtils类用于操作数据库的工具类。

3.1 后端逻辑

  1. 前后端交互的数据约定为 json格式,在后端实现中也是通过导入的jackson库 解析该数据。
  2. 点击提交:浏览器将表白信息发送到服务器,对应着一对请求(Post)和响应
  3. 页面加载:浏览器从服务器哪获取到我们之前输入过的表白信息显示在页面,也对应一组请求(Get)与响应,该响应的body部分 通过json数组存储json格式的每条数据(json对象)。

由此我们在MassageServlet代码中只用实现 doPost()方法向服务器发送数据,doGet()方法从服务器获取数据,save()方法调用DBUtils类向数据库存储信息,load()方法从数据库取所有信息;

3.2 后端代码

MassageServlet.class:

  1. 在该类中 @WebServlet(“/message”) 对应着我们前端页面通过ajax构造请求发送的url路径一致,此处我们写的是相对路径。
  2. 导入jackson库之后我们就可以通过ObjectMapper类的readValue()读取请求中body的数据并规定以Massage.class格式读取,然后赋值给massage;

    Message message = objectMapper.readValue(req.getInputStream(),Message.class);

  3. massage.class,该类是为了存储读取到的json格式的键值对数据而创建。

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

    /**

    • @author zq
      */
      class Message{

      public String from;
      public String to;
      public String message;
      }

    @WebServlet(“/message”)

    public class MessageServlet extends HttpServlet {

    1. //private List<Message> messageList = new ArrayList<>();
    2. //向服务器发送数据
    3. @Override
    4. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    5. ObjectMapper objectMapper = new ObjectMapper();
    6. //读取body中的内容赋值给massage对象
    7. Message message = objectMapper.readValue(req.getInputStream(),Message.class);
    8. //通过简单粗暴的方法保存,list
    9. //messageList.add(message);
    10. save(message);
    11. //设置响应状态码(可以不设置)
    12. resp.setStatus(200);
    13. }
    14. //从服务器获取数据
    15. @Override
    16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    17. resp.setContentType("application/json;charset=utf8");
    18. ObjectMapper objectMapper = new ObjectMapper();
    19. List<Message> messageList = load();
    20. // objectMapper.writeValue(resp.getWriter(),messageList);
    21. objectMapper.writeValue(resp.getWriter(),messageList);
    22. }
    23. //提供一对方法
    24. //王数据库存一条消息
    25. private void save(Message message) {
    26. //这是基本的jdbc操作
    27. //1.建立连接
    28. Connection connection = null;
    29. PreparedStatement statement =null;
    30. try {
    31. connection = DBUtil.getConnection();
    32. //2.构造sql语句
    33. String sql = "insert into message value(?,?,?)";
    34. statement = connection.prepareStatement(sql);
    35. statement.setString(1,message.from);
    36. statement.setString(2,message.to);
    37. statement.setString(3,message.message);
    38. //3.执行sql
    39. statement.executeUpdate();
    40. } catch (SQLException e) {
    41. e.printStackTrace();
    42. }finally {
    43. //4.关闭连接
    44. DBUtil.close(connection,statement,null);
    45. }
    46. }
    47. //从数据库取所以消息
    48. private List<Message> load(){
    49. List<Message> messageList = new ArrayList<>();
    50. //1.建立连接
    51. Connection connection = null;
    52. PreparedStatement statement =null;
    53. ResultSet resultSet = null;
    54. try {
    55. connection = DBUtil.getConnection();
    56. //2.构造sql语句
    57. String sql = "select * from message ";
    58. statement = connection.prepareStatement(sql);
    59. //3.执行sql
    60. resultSet = statement.executeQuery();
    61. //4.遍历结果集合
    62. while (resultSet.next()){
    63. Message message = new Message();
    64. //根据列名取出sql语句
    65. message.from = resultSet.getString("from");
    66. message.to = resultSet.getString("to");
    67. message.message = resultSet.getString("message");
    68. messageList.add(message);
    69. }
    70. } catch (SQLException e) {
    71. e.printStackTrace();
    72. }finally {
    73. //4.关闭连接
    74. DBUtil.close(connection,statement,resultSet);
    75. }
    76. return messageList;
    77. }

    }

DBUtils.class:

  1. import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
  2. import javax.sql.DataSource;
  3. import java.sql.Connection;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. /**
  8. * @author zq
  9. */
  10. /*通过这个类将数据库连接封装起来
  11. * 此处将DBUtil作为一个工具类,提供static方法共其他代码使用
  12. * */
  13. public class DBUtil {
  14. //静态成员跟随类对象,类对象在整个进程中只有唯一一份
  15. //静态成员相对于也是唯一实例,(单例模式,饿汉模式)
  16. private static DataSource dataSource = new MysqlDataSource();
  17. static{
  18. //使用静态代码块,针对dataSource进行初始化操作
  19. ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/javaee?characterEncoding=utf8&useSSL=false");
  20. ((MysqlDataSource)dataSource).setUser("root");
  21. ((MysqlDataSource)dataSource).setPassword("123456");
  22. }
  23. //通过该方法进行建立连接
  24. public static Connection getConnection() throws SQLException {
  25. return dataSource.getConnection();
  26. }
  27. //通过这个方法进行释放资源
  28. public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
  29. //此处三个try-catch分开写更好
  30. // 避免前面的出问题后面的执行不了
  31. if (resultSet !=null){
  32. try {
  33. resultSet.close();
  34. } catch (SQLException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. if (preparedStatement != null){
  39. try {
  40. preparedStatement.close();
  41. } catch (SQLException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. if (connection != null){
  46. try {
  47. connection.close();
  48. } catch (SQLException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. }
  53. }

发表评论

表情:
评论列表 (有 0 条评论,97人围观)

还没有评论,来说两句吧...

相关阅读