前言
前面我们学习了 Java 中知名的 HTTP 服务器 tomcat 的安装和使用,还学习了 servlet 相关 API 的学习,今天,这篇文章我们将运用前面学习的 HTTP 知识、tomcat和servlet来实现一个简单的表白墙网站。
效果展示
我先为大家展示一下这个表白墙网站完成之后的最终效果。
首先当我们访问表白墙网站的 HTML 页面的时候,得到的是这个结果。
当我们输入信息并且提交的时候,会将输入的信息进行处理,然后显示在这个页面的下面。
然后我们就可以根据上面的效果来逐步实现代码。
事前准备
在这里我给大家说明一下:要想实现网站,不仅需要后端的知识,还需要一些像什么 HTML、CSS、JavaScript这样的前端知识,而很多人可能还没接触过前端,大家不用慌,我也是刚接触前端,本篇文章我将为大家简单的介绍一下关于前端的时候,让大家大概知道我前段的每一段代码都是干啥的就行了。并且,我也是一个前端小白,如果我的分享有错误的话,欢迎大家在评论区或者私信我指出来,我在这里谢谢大家了。
首先我们需要创建出一个 Maven 项目,然后根据 tomcat 的要求创建出符合要求的目录结构,以及配置一些文件中的内容。
创建出符合 tomcat 标准的目录结构。
在 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>
创建出一个 MessageServlet.java 文件用来写我们的后端主要的代码。
HTML、CSS、JavaScript分别负责哪些
HTML、CSS 和 JavaScript 是构建网页的三种主要技术,它们分别负责网页的结构、样式和行为。
HTML (HyperText Markup Language):HTML 是网页的基础,它负责创建和组织网页的内容。HTML 是一种标记语言,它使用各种标签来定义网页中的不同元素,例如标题、段落、链接、图片等。
CSS (Cascading Style Sheets):CSS 负责网页的样式和布局。它可以改变文本的颜色、字体和大小,也可以调整元素的位置、大小和边距等。
JavaScript:JavaScript 负责网页的行为和交互。它可以动态地更改网页的内容、响应用户的点击和输入,甚至加载新的内容。
简单的讲,HTML的作用就是决定你这个网页有哪些基本的结构,就是一个人有一个嘴巴、两个眼睛、一个鼻子;CSS则是决定你某一结构的具体样式和布局,就是一个人是双眼皮、高鼻梁、小嘴巴;而JavaScript则决定你网页的行为和交互,你怎么吃饭的、怎么呼吸的。
HTML和CSS构架出页面的基本结构和样式
当做好前面的准备之后,我们就需要实现表白墙网站的第一步:显示出基本页面。这个显示出页面主要用到了 HTML和CSS的知识。我们的这个 html 文件需要放在 webapp 目录下。
<!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> </body> </html>
CSS中的通配符选择器(*),它选中了页面上的所有元素,然后将所有元素的margin(外边距)、padding(内边距)设置为0,并将box-sizing属性设置为border-box。
这段代码的目的主要是重置网页上所有元素的默认样式。在很多情况下,浏览器会对HTML元素应用默认样式,这些默认样式可能会影响到网页的布局和元素的尺寸。通过将margin和padding设置为0,可以消除元素之间的间距(margin)和内部填充(padding),而将box-sizing设置为border-box可以使元素的宽度和高度包括其边框和内边距。
它定义了一个名为".container"的类。这个类设置了一个元素的宽度为600像素,并将外边距(margin)设置为20像素,且上下外边距为自动(auto),使得这个元素在页面上居中。
用于设置HTML文档中的h1和p元素的样式。也就是h1标签和p标签。
- h1元素内的文本应该居中显示(text-align: center;)。
- p元素内的文本也应该居中显示,颜色为#666(一个深灰色),并且上下边距为20px。
用于设置一个名为".row"的类的样式。这段代码使用Flexbox模型来定义行内元素的布局和对齐方式。
display: flex;:这会使得元素变为弹性容器,其子元素会按Flexbox模型布局。
height: 40px;:这会设置元素的高度为40像素。
justify-content: center;:这会使得行内元素在主轴(水平方向)上居中对齐。
align-items: center;:这会使得行内元素在交叉轴(垂直方向)上居中对齐。
所有属于.row类的元素的span子元素,其宽度将被设置为80px。
所有属于.row类的元素的input子元素,其宽度将被设置为200px,高度将被设置为30px。这个就是我们输入框的相关属性。
所有属于.row类的元素的button子元素,其宽度将被设置为280px,高度将被设置为30px,文本颜色将被设置为白色,背景颜色将被设置为橙色,边框将被移除,并且边框半径(用于创建圆角)将被设置为5px。这个就是点击按钮的相关属性。
JavaScript 实现行为和交互
上面主要用到了我们的 HTML 和 CSS 相关的知识,基本构建出了页面的基本结构和样式,而接下来,我们将使用 JavaScript 来完成提交信息这个动作。
首先需要构建出container类中的各个输入框的行为。
<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>
<div class=“container”>:开始一个容器div,用于包装整个页面的内容。创建出container这个类。
<h1>表白墙:创建一个主标题,显示“表白墙”字样。
<p>输入内容后点击提交, 信息会显示到下方表格中:创建一个段落,用于向用户说明他们可以输入表白信息,然后点击提交按钮,信息会显示在下方的表格中。
<div class=“row”>:开始一个行div,用于包装每一条表白信息。
<span>谁: :创建一个标签span,显示“谁:”字样,用于提示用户输入表白者的名字。
<input type=“text”>:创建一个文本输入框,用户可以在这里输入他们的名字。test表示输入的是文本。
<div class=“row”>:开始最后一个行div。
<button id=“submit”>提交:创建一个按钮,id为“submit”,用于提交表单。按钮上显示“提交”字样。
以上的JavaScript完成了输入框和提交按钮的行为,而JavaScript还需要完成点击提交按钮之后将数据经过处理然后显示在页面上。
<script> let containerDiv = document.querySelector('.container');] let inputs = document.querySelector('.input'); let button = document.querySelector('#submit'); button.onclick = function() { //获取到三个输入框的内容 let from = inputs[0]; let to = inputs[1]; let message = input[2]; //判断是否未输入 if (from == '' || to == '' || message == '') { return; } //创建出一个新的元素 let rowDiv = document.createElement('div'); //定义出rowDiv的类名 rowDiv.className = 'row message'; //为rowDiv中插入元素 rowDiv.innerHtml = from + ' 对 ' + to + ' 说: ' + msg; //将这个新创建的元素添加到containerDiv类的末尾 containerDiv.appendChild(rowDiv); //将输入框中的内容置为空 for (let input of inputs) { input.value = ''; } //构造出一个对象用来存储刚才输入框输入的内容,并且这个内容是以json的格式存在的 let requestBody = { "from": from, "to": to, "message": message } //通过我们前面引入的Jackson依赖,使用JSON中的stringify方法将对象转换为json let jsonString = JSON.stringify(requestBody); //$是我们前面引入的jQuery依赖中的全局变量,通过这个$可以调用jQuery中的很多方法 //通过这个ajax方法构造请求,并且发送给服务器 $.ajax({ type: 'post', url: 'message', contentType: 'application/json; charset=utf8'; data: jsonString, success: function(responseBody) { console.log(responseBody); } }); } </script>
Ajax属于第三方库,所以我们要想使用的话,就需要引入相关依赖。引入jQuery库。https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js
实现服务器端的业务
当前端构造出 post 请求发送给服务器,服务器接收到这个请求之后就需要根据这个发送来的请求做出业务处理。因为我们使用的是 Servlet 实现的,所以就需要先引入 Servlet 依赖。
因为前端构造请求的数据格式是 json 的数据格式,所以要想在 Java 中使用 json,也就需要引入 json 库,或者包装了 json 库的其他库。
引入Jackson依赖。
当引入 Servlet 和 Jackson 依赖之后,我们服务端就可以通过 Servlet 的相关 API 做出相应的业务处理了。
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; class Message { public String from; public String to; public String message; @Override public String toString() { return "Message{" + "from='" + from + '\'' + ", to='" + to + '\'' + ", message='" + message + '\'' + '}'; } } @WebServlet("/message") public class MessageServlet extends HttpServlet { private ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置相应的状态码 resp.setStatus(200); //通过ObjectMapper中的readValue方法将json数据转换为Java对象 Message message = objectMapper.readValue(req.getInputStream(), Message.class); //将客户端输入的信息进行存储 save(message); System.out.println(message); resp.getWriter().write("ok"); } }
当客户端进行提交请求之后,前端会将输入的信息传给服务器,那么当服务器接收到这个请求之后,应该将输入存储在哪个地方呢?如果只是拿一个简单的 List 容器存储数据话,那么这个数据就是存储在内存中的,当服务器重启之后,之前存储的数据就会消失,那么该如何存储才能保证数据的持久性呢?这里想到的肯定就是数据库了,数据库天然支持数据存储的持久性,所以我们这里选择使用 MySQL 数据库来实现数据的存储。
-- 创建数据库 create database if not exists message_wall; -- 选中数据库 use message_wall; -- 为了防止这个表存在对我们的数据库造成影响,我们先删除数据库 drop table if exists message; -- 创建表 create table message(`from` varchar(1024), `to` varchar(1024), message varchar(1024));
当创建完成数据库和表之后,我们继续要引入 MySQL 依赖,然后实现 JDBC 编程了。
这里因为我的 MySQL 版本是 MySQL8,所以依赖选择的也是 MySQL8 版本,大家需要根据自己的 MySQL 版本来导入对应的依赖。
import com.fasterxml.jackson.databind.ObjectMapper; import com.mysql.cj.jdbc.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.SQLException; class Message { public String from; public String to; public String message; @Override public String toString() { return "Message{" + "from='" + from + '\'' + ", to='" + to + '\'' + ", message='" + message + '\'' + '}'; } } @WebServlet("/message") public class MessageServlet extends HttpServlet { private ObjectMapper objectMapper = new ObjectMapper(); private DataSource dataSource = new MysqlDataSource(); @Override public void init() throws ServletException { ((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/message_wall?characterEncoding=utf8&useSSl=false"); ((MysqlDataSource)dataSource).setUser("root"); ((MysqlDataSource)dataSource).setPassword("*******"); //这里是我们的MySQL密码 } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置相应的状态码 resp.setStatus(200); //通过ObjectMapper中的readValue方法将json数据转换为Java对象 Message message = objectMapper.readValue(req.getInputStream(), Message.class); //将客户端输入的信息进行存储 try { save(message); } catch (SQLException e) { throw new RuntimeException(e); } System.out.println(message); resp.getWriter().write("ok"); } private void save(Message message) throws SQLException { Connection connection = dataSource.getConnection(); 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); statement.executeUpdate(); statement.close(); connection.close(); } }
当实现完成将客户端输入的数据存储进MySQL数据库之后,当我们进入这个网站的时候,还需要将数据库已经存在的信息给读取到页面上,所以,当我们访问这个网站的时候,浏览器会向服务器发送一个 Ajax 请求,当服务器接收到这个 Ajax 请求的时候就会将数据库中的数据给返回给客户端。
$.ajax({ type: 'get', url: 'message', success: function(body) { let containerDiv = document.querySelector('.container'); for (let i = 0; i < body.length; i++) { let message = body[i]; let div = document.createElement('div'); div.className = 'row'; div.innerHTML = message.from + "对" + message.to + "说" + message.message; containerDiv.appendChild(div); } } });
我们这个标签放在 <script> 标签下,当访问这个 html 文件的时候,就会自动向服务器发送一个请求。
而当我们的服务器接收到这个请求的时候,便会将数据库中存在的数据返回给客户端。
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(200); resp.setContentType("application/json; charset=utf8"); List<Message> messageList = null; try { messageList = load(); } catch (SQLException e) { throw new RuntimeException(e); } String jsonString = objectMapper.writeValueAsString(messageList); resp.getWriter().write(jsonString); } private List<Message> load() throws SQLException { Connection connection = dataSource.getConnection(); String sql = "select * from message"; PreparedStatement statement = connection.prepareStatement(sql); ResultSet resultSet = statement.executeQuery(); 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); } statement.close(); connection.close(); return messageList; }
代码整理
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>message_wall1</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> <!-- 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/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> </dependencies> </project>
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>
messageWall.html
<!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 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); } }); } $.ajax({ type: 'get', url: 'message', success: function(body) { let containerDiv = document.querySelector('.container'); for (let i = 0; i < body.length; i++) { let message = body[i]; let div = document.createElement('div'); div.className = 'row'; div.innerHTML = message.from + "对" + message.to + "说" + message.message; containerDiv.appendChild(div); } } }); </script> </body> </html>
MessageServlet.java
import com.fasterxml.jackson.databind.ObjectMapper; import com.mysql.cj.jdbc.MysqlDataSource; import sun.dc.pr.PRError; 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; class Message { public String from; public String to; public String message; @Override public String toString() { return "Message{" + "from='" + from + '\'' + ", to='" + to + '\'' + ", message='" + message + '\'' + '}'; } } @WebServlet("/message") public class MessageServlet extends HttpServlet { private ObjectMapper objectMapper = new ObjectMapper(); private DataSource dataSource = new MysqlDataSource(); @Override public void init() throws ServletException { ((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/message_wall?characterEncoding=utf8&useSSl=false"); ((MysqlDataSource)dataSource).setUser("root"); ((MysqlDataSource)dataSource).setPassword("lmh041105666"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(200); resp.setContentType("application/json; charset=utf8"); List<Message> messageList = null; try { messageList = load(); } catch (SQLException e) { throw new RuntimeException(e); } String jsonString = objectMapper.writeValueAsString(messageList); resp.getWriter().write(jsonString); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置相应的状态码 resp.setStatus(200); //通过ObjectMapper中的readValue方法将json数据转换为Java对象 Message message = objectMapper.readValue(req.getInputStream(), Message.class); //将客户端输入的信息进行存储 try { save(message); } catch (SQLException e) { throw new RuntimeException(e); } System.out.println(message); resp.getWriter().write("ok"); } private List<Message> load() throws SQLException { Connection connection = dataSource.getConnection(); String sql = "select * from message"; PreparedStatement statement = connection.prepareStatement(sql); ResultSet resultSet = statement.executeQuery(); 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); } statement.close(); connection.close(); return messageList; } private void save(Message message) throws SQLException { Connection connection = dataSource.getConnection(); 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); statement.executeUpdate(); statement.close(); connection.close(); } }