结合servlet API,实现一个服务器版本的表白墙程序. 即使页面关闭, 表白墙的内容也不会丢失(实现持久化存储数据)
1. 准备工作
- 创建 maven 项目.
- 创建必要的目录 webapp, WEB-INF, web.xml
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>
- 调整 pom.xml
引入依赖, 配置生成 war 包, 以及 war 包名字
<?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_wall</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- servlet驱动包-->
<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>
<!-- Jackson驱动包-->
<!-- 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>
<!-- 引入MySQL驱动包 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
</project>
- 实现表白墙前端页面拷贝到 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>
</head>
<body>
<style>
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container{
width: 100%;
}
h3{
text-align: center;
padding: 20px 0;
font-size: 30px;
}
p{
text-align: center;
color: #666;
padding: 10px 0;
}
.row {
width: 400px;
height: 50px;
margin: 0 auto;
display:flex;
justify-content: center;
align-items: center;
}
.row span{
width: 100px;
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: 40ox;
margin: 0 auto;
color: white;
background-color: orange;
border: none;
border-radius: 10px;
}
.row #submit:active{
background-color: grey;
}
</style>
<h3>表白墙</h3>
<p>输入后点击提交,信息会显示在表格中</p>
<div class="container">
<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>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
//当用户点击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 mesg=inputs[2].value;
if(from==''||to==''||mesg==''){
//用户还没填写完,暂时先不提交数据
return;
}
//2.生成一个新的div,内容就是input里的内容,把这个新的div加到页面中
let div=document.createElement('div');
div.innerHTML=from+'对'+to+'说'+mesg;
div.className='row';
let container=document.querySelector('.container');
container.appendChild(div);
//3.提交后清空原有数据操作
for(let i=0;i<inputs.length;i++){
inputs[i].value='';
}
</script>
</body>
</html>
2. 约定前后端交互接口
“前后端交互接口” 是进行 Web 开发中的关键环节.
具体来说, 就是允许页面给服务器发送哪些 HTTP 请求, 并且每种请求预期获取什么样的 HTTP 响应.
2.1 获取全部留言功能接口
期望浏览器给服务器发送一个 GET /message 这样的请求, 就能返回当前一共有哪些留言记录. 结果以 json 的格式返回过来
请求:
GET /message
响应: JSON 格式
[
{
from: “xx”,
to: “xx”,
message: “x”
},
{
from: “xxx”,
to: “xxx”,
message: “xx”
},
…
]
2.2 发表新留言功能接口
期望浏览器给服务器发送一个 POST /message 这样的请求, 就能把当前的留言提交给服务器.
请求: body 也为 JSON 格式
POST /message
{
from: “xxx”,
to: “xxx”,
message: “xx”
}
响应: JSON 格式.
{
ok: true
}
3. 服务器端代码整体如下
// import具体省略
class Message {
public String from;
public String to;
public String message;
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
// private List<Message> messages=new ArrayList<>();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理提交消息的请求
Message message=objectMapper.readValue(req.getInputStream(),Message.class);
// 最简单的保存方法就是保存到内存中.
// messages.add(message);
// 通过 ContentType 来告知页面, 返回的数据是 json 格式.
// 有了这样的声明, 此时 jquery ajax 就会自动的帮我们把字符串转成 js 对象.
// 如果没有, jquery ajax 就只是当成字符串来处理的~~
save(message);
resp.setContentType("application/json; charset=utf8");
resp.getWriter().write("{\"ok\":true}");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取到消息列表
// 获取到消息列表. 只要把消息列表中的内容整个的都返回给客户端即可
// 此处需要使用 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 message 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 {
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 message";
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;
}
}
ObjectMapper
的readValue
方法能直接从一个InputStream
对象读取数据.,将json格式字符串转成Java对象ObjectMapper
的writeValueAsString
方法也能把一个对象数组直接转成 JSON 格式的字符串
4. 调整前端页面代码
加上ajax发送http请求给服务器的前端页面代码如下:
<!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: 20px 0;
font-size: 30px;
}
p{
text-align: center;
color: #666;
padding: 10px 0;
}
.row {
width: 400px;
height: 50px;
margin: 0 auto;
display:flex;
justify-content: center;
align-items: center;
}
.row span{
width: 100px;
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: 40ox;
margin: 0 auto;
color: white;
background-color: orange;
border: none;
border-radius: 10px;
}
.row #submit:active{
background-color: grey;
}
</style>
<h3>表白墙</h3>
<p>输入后点击提交,信息会显示在表格中</p>
<div class="container">
<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>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
//加入ajax的代码,此处要加入的逻辑有两个部分
//点击提交按钮的时候,ajax要构造数据发送给服务器
//页面加载时候,从服务器获取消息列表,显示在页面上
function getMessages(){
$.ajax({
type:'get',
url:'message',
success:function(body){
//当前body已经是一个js对象数组了,ajax会根据相应的content type来进行自动解析
//如果服务器返回的content type已经是applocation/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);
}
}
});
}
//不要忘记函数调用!
getMessages();
//当用户点击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 mesg=inputs[2].value;
if(from==''||to==''||mesg==''){
//用户还没填写完,暂时先不提交数据
return;
}
//2.生成一个新的div,内容就是input里的内容,把这个新的div加到页面中
let div=document.createElement('div');
div.innerHTML=from+'对'+to+'说'+mesg;
div.className='row';
let container=document.querySelector('.container');
container.appendChild(div);
//3.提交后清空原有数据操作
for(let i=0;i<inputs.length;i++){
inputs[i].value='';
}
//4.把当前获取到的输入框内容,构造成一个post请求,通过ajax发送给服务器
let body={
from:from,
to:to,
message:mesg
};
$.ajax({
type: "post",
url: "message",
contentType: "application/json;charset=utf8",
// 把body转json格式的字符串
data: JSON.stringify(body),
success:function(body){
alert("消息提交成功!")
},
error:function(body){
alert("消息提交失败!")
}
});
}
</script>
</body>
</html>
此时我们每次提交的数据都会发送给服务器. 每次打开页面的时候页面都会从服务器加载数据. 因此及时
关闭页面, 数据也不会丢失.
6. 数据存入数据库
可以借助数据库完成存储工作,此时即使重启服务器, 留言数据也不会丢失了.
- 在 pom.xml 中引入 mysql 的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
- 创建数据库, 创建 messages 表,表结构如下:
- 创建 DBUtil 类
DBUtil 类主要实现以下功能:
- 创建
MysqlDataSource
实例, 设置 URL, username, password 等属性.
提供getConnection
方法, 和 MySQL 服务器建立连接.
提供close
方法, 用来释放必要的资源.
代码如下:
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DBUtil {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/java102?characterEncoding=utf8&useSSL=false";
private static final String USERNAME = "root";
private static final String PASSWORD = "1963";
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 (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- 实现 load 和 save 方法, 改成操作数据库 :
save方法:
private void save(Message message){
//把一条消息保存到数据库中
Connection connection=null;
PreparedStatement statement=null;
try {
//1.和数据库建立连接
connection=DBUtil.getConnection();
//2.构造SQL
String sql="insert into message 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 {
DBUtil.close(connection,statement,null);
}
}
load方法:
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 message";
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;
}
doget方法和dopost方法需要注意的点如下:
重新部署程序, 此时使用数据库之后也可以保证即使服务器重启, 数据也不丢失
7. 整体效果演示 :
因为之前已经提交过四次操作,此时一打开页面就能显示之前提交的记录:
提交相应的记录,数据库记录相应增加
所有功能均实现成功无异常~
- over~✨