Servlet的使用(JavaEE初阶系列17)

目录

前言:

1.Servlet API的使用

1.1HttpServlet

1.2HttpServletRequest

1.3HttpServletResponse

2.表白墙的更新

2.1表白墙存在的问题

2.2前后端交互接口

2.3环境准备

2.4代码的编写

2.5数据的持久化

2.5.1引入JDBC依赖

2.5.2创建数据库

2.5.3编写数据库代码

3.Cookie回忆

4.Session机制

4.1getSession

4.2模拟实现登录功能

4.2.1登录页的核心代码。

4.2.2编写登录页面的Servlet

4.2.3编写生成主页的Servlet

4.2.4起启动浏览器

结束语:


前言:

上一节中小编主要与大家分享了有关于Tomcat和Servlet的基础的一些知识,接下来小编将会给大家介绍一下有关于Servlet的API以及对上一次编写的表白墙案例的更新。话不多说一起来学起来吧!

1.Servlet API的使用

Servlet中的API有很多这里我们只需要掌握里面的三个即可。接下来我们来具体看一下。

1.1HttpServlet

我们在写Servlet代码的时候,首先第一步就是先创建类,继承自HttpServlet,并重写其中的某些方法。

核心方法:

方法名称调用时机
init在HttpServlet实例化之后被调用一次
destory在HttpServlet实例不再使用的时候会调用一次
service收到HTTP请求的时候调用
doGet收到GET请求的时候调用(由service方法调用)
doPost收到POST请求的时候调用(由service方法调用)
doPut/doDelete/doOptions收到其他请求的时候调用(由service方法调用)

Servlet的生命周期:

  • init:是在初识情况下调用一次。
  • destory:是在结束之前调用一次。
  • service:每次收到路径匹配的请求都要调用一次。

代码展示:

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;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("这是一个doGet方法");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("这是一个doPost方法");}@Overrideprotected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("这是一个doPut");}@Overrideprotected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("这是一个doDelete方法");}
}

结果展示:

1.2HttpServletRequest

它是一个HTTP请求,当Tomcat通过Socket API读取到HTTP请求之后就会解析生成上述的HttpServletRequest对象。这里就是HTTP请求报文里有啥,这里类里面就会有啥。

核心方法:

方法

描述

String getProtocol()

返回请求协议的名称和版本。
String getMethod()返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
String getRequestURI()

从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请

求的 URL 的一部分。

String getContextPath()返回指示请求上下文的请求 URI 部分。
String getQueryString()返回包含在路径后的请求 URL 中的查询字符串。

Enumeration

getParameterNames()

返回一个 String 对象的枚举,包含在该请求中包含的参数的名

称。

String getParameter(String

name)

以字符串形式返回请求参数的值,或者如果参数不存在则返回

null。

String[]

getParameterValues(String

name)

返回一个字符串对象的数组,包含所有给定的请求参数的值,如

果参数不存在则返回 null。

Enumeration

getHeaderNames()

返回一个枚举,包含在该请求中包含的所有的头名。

String getHeader(String

name)

以字符串形式返回指定的请求头的值。

String

getCharacterEncoding()

返回请求主体中使用的字符编码的名称。
String getContentType()返回请求主体的 MIME 类型,如果不知道类型则返回 null。
int getContentLength()

以字节为单位返回请求主体的长度,并提供输入流,或者如果长

度未知则返回 -1。

InputStream

getInputStream()

用于读取请求的 body 内容. 返回一个 InputStream 对象。

注意:上述中有一个是URI,注意不是URL,URL是唯资源定位符,而URI是唯一资源标识符,这两概念非常相似,以至于很多时候我们都是直接混着使用了。

代码展示:

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.Enumeration;@WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {StringBuilder result = new StringBuilder();//返回协议请求的名称和版本result.append(req.getProtocol());result.append("<br>");//返回请求的HTTP方法的名称,例如GET、POST或PUTresult.append(req.getMethod());result.append("<br>");//从协议名称直到HTTP请求的第一行的查询字符中,返回该请求的URL的一部分。result.append(req.getRequestURI());result.append("<br>");//返回包含在路径后的请求URL中的查询字符串result.append(req.getQueryString());result.append("<br>");//返回请求指示上下文的请求URI部分result.append(req.getContextPath());result.append("<br>");result.append("========================<br>");//返回一个枚举类,包含在该请求中所有的头名Enumeration<String> headerNames = req.getHeaderNames();while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();String headerValue = req.getHeader(headerName);result.append(headerName + ":" + headerValue + "<br>");}//在响应中设置上body的类型,方便浏览器进行解析resp.setContentType("text/html;charset=utf8");resp.getWriter().write(result.toString());}
}


结果展示:

其中重点给大家讲解一下getParameter,它是最常用的API之一,在开发中前端给后端传递数据的时候是非常常见的需求。在传递中主要有以下三种传递方式:

  • 通过query string传递
  • 通过body(form)
  • 通过body(json)

下面我们来具体看一下。

①通过query string来传递

此时我们约定前端是通过query string来传递username和password的。

代码展示:

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;@WebServlet("/getParameter")
public class GetParameter extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//前端通过url的query传递username和password两个属性String username = req.getParameter("username");if (username == null) {System.out.println("username这个key在query string中不存在");}String password = req.getParameter("password");if (username == null) {System.out.println("password这个key在query string中不存在");}System.out.println("username=" + username + ", password=" + password);resp.getWriter().write("ok");}
}
 

结果展示:

在服务器上输入一下请求之后得到的结果是:

​​​​​​http://127.0.0.1:8080/hello_servlet/getParameter​​​​​​

 

如果输入的是下面的请求之后得到的结果是:

http://127.0.0.1:8080/hello_servlet/getParameter?username=zhangsan&password=123


注意:query string中的键值对都是程序猿来自定义的。所以这样就可以根据需求来定义了。

②通过body(form)

相当于body里存档数据格式和query string一样,但是Content-Type是application/x-www-form-urlencoed,此时也是通过getParameter来获取到键值对的。这里我们是借助与postman来构造一个请求的。如下所示:

代码展示:

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;@WebServlet("/getParameter")
public class GetParameter extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//前端通过body,以form表单的格式,把username和password传递给服务器String username = req.getParameter("username");if (username == null) {System.out.println("username这个key在body中不存在");}String password = req.getParameter("password");if (username == null) {System.out.println("password这个key在body中不存在");}System.out.println("username=" + username + ", password=" + password);resp.getWriter().write("ok");}
}

结果展示:

 

但是如果我们这里输入的是中文的张三会有啥问题呢?

在url中query string如果是包含中文或者是特殊字符,那么请务必使用urlencode(☞在线URL编码urlencode工具 - UU在线工具)来进行转码,如果是直接写中文会存在很大的风险。如果不转码,在有些浏览器中/http服务器下对中文支持不好的话,就会出现问题。

转码如下所示:

 

 那么这里我们就直接将转码之后的直接放在url的构造中,如下所示:

http://127.0.0.1:8080/hello_servlet/getParameter?username=%E5%BC%A0%E4%B8%89&password=123

这样在结果中他会自动转回来。 

但是如果我们是在postman中使用中文就会出现如下情况。

一般我们在使用postman的时候是utf8的,所以此时我们就需要显示的告诉后端代码请求使用的是utf编码。

代码展示:

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;@WebServlet("/getParameter")
public class GetParameter extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//给请求设置utf8req.setCharacterEncoding("utf8");//前端通过body,以form表单的格式,把username和password传递给服务器String username = req.getParameter("username");if (username == null) {System.out.println("username这个key在body中不存在");}String password = req.getParameter("password");if (username == null) {System.out.println("password这个key在body中不存在");}System.out.println("username=" + username + ", password=" + password);resp.getWriter().write("ok");}
}

结果展示:

设置之后就解决了乱码的问题。

③通过body(json)

通过body(json)是最常见的方式,这里需要我们重点掌握。json也是一个键值对的格式,但是Servlet自身没有内置json的解析功能,所以这里我们就需要借助其他的第三方库了。这里我们使用的是jackson。我们直接从maven中央仓库来下载。具体步骤看下面。

① 搜索jackson选择第一个。

②然后随便选择一个版本。 


③直接复制maven到dependencies中。

④点击刷新maven。 

这里我们使用postman来构造请求。

代码展示:

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 User {public String username;public String password;
}
@WebServlet("/json")
public class JsonServlet extends HttpServlet {//使用Jackson,最核心的对象就是ObjectMapper//通过这个对象,就可以把json字符串解析成java对象,也可以把一个java对象 转化成一个json格式的字符串private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//通过post请求的body传递过来的一个json格式的字符串//第一个参数是表示对谁进行解析//第二个参数表示要将传入的参数解析成什么样的java对象。User user = objectMapper.readValue(req.getInputStream(),User.class);System.out.println("username= " + user.username + ",password =" + user.password);resp.getWriter().write("OK");}
}


结果展示:

解析readValue里面做的事情:

①解析json字符串,转换成若干个键值对。

②根据第二个参数User.class去找到User里面的的所有的public的属性或者是有public的getter和setter的属性依次进行遍历。

③遍历属性,根据属性的名字,去上述准备好的键值对里查询看看这个属性的名字是否存在对应的value里,如果存在就把value赋值到该属性中。 

1.3HttpServletResponse

Servlet中的doXXX方法的目的就是根据请求计算得到响应,然后把响应的数据设置到HttpServletResponse对象中,然后Tomcat就会把这个HttpServletResponse对象按照HTTP协议的格式,转换成一个字符串,并通过Socket写回给浏览器。

核心方法:

方法

描述

void setStatus(int sc)

为该响应设置状态码。

void setHeader(String name,

String value)

设置一个带有给定的名称和值的 header. 如果 name 已经存在,

则覆盖旧的值.

void addHeader(String

name, String value)

添加一个带有给定的名称和值的 header. 如果 name 已经存在,

不覆盖旧的值, 并列添加新的键值对

void setContentType(String

type)

设置被发送到客户端的响应的内容类型。

void

setCharacterEncoding(String

charset)

设置被发送到客户端的响应的字符编码(MIME 字符集)例如,

UTF-8。

void sendRedirect(String

location)

使用指定的重定向位置 URL 发送临时重定向响应到客户端。

PrintWriter getWriter()

用于往 body 中写入文本格式数据.

OutputStream

getOutputStream()

用于往 body 中写入二进制格式数据.

代码展示:

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;@WebServlet("/status")
public class StatusServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//为该响应设置状态码resp.setStatus(404);//设置被发送到客户端的响应的内容类型resp.setContentType("text/html; charset=utf8");resp.getWriter().write("返回404响应!");}
}

结果展示:

自动重定向:

代码展示:

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;@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//用户访问这个路径的时候,会自动重定向到搜狗主页
//        resp.setStatus(302);
//        //设置一个带有给定的名称和值的header,如果name已经存在,则覆盖旧的值。
//        resp.setHeader("Location","https://www.soguo.com");//使用指定的重定向位置url发送临时重定向响应到客户端resp.sendRedirect("https://www.sogou.com/");}
}
 

结果展示:

页面将自动跳转到搜狗的页面。

通过header来实现自动刷新的效果。

代码展示:

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;@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//设置为每隔1s刷新一次resp.setHeader("Refresh","1");resp.getWriter().write("time=" + System.currentTimeMillis());}
}

结果展示:

2.表白墙的更新

之前写的前端表白墙的博客链接☞http://t.csdn.cn/NdwmC

2.1表白墙存在的问题

我们之前只是实现了表白墙的前端,它存在最大的问题就是1.页面重启之后数据会丢失,2.数据只是在本地,别人是看不到的。

那么针对上述的两个问题我们只需要引入后端服务器即可,当用户打开页面的时候,就需要从服务器加载当前已经提交过的数据,当用户新增数据到时候也会把数据交给服务器,让服务器持久化保存。

2.2前后端交互接口

这里当我在写后端代码的时候我们先来明确前后端的交互接口。

  1. 页面加载完毕之后,需要给服务器发一个请求获取到当前的留言数据。
  2. 用户点击提交的时候就需要告诉服务器,当前用户发了的消息是啥。

 这在交互的过程中涉及到了一个重要的问题,请求和响应具体是啥样子的,这里都需要我们来做出一个约定。

注意:json中使用[]表示数组,[]中的每一个元素,是一个{}json对象,每个对象里,又有三个属性from,to,message。

对于接口二服务器要做的事情就是解析请求中的body,转成message对象,然后把这个message对象给保存起来。

2.3环境准备

①创建maven项目,然后处理处理静态页面,将静态页面导入到项目中。

静态页面内的代码可以参考小编之前的一篇博客里面的表白墙的代码 ☞

JavaScript(JavaEE初阶系列13)_奶油酒窝✧٩(ˊωˋ*)و✧的博客-CSDN博客

2.4代码的编写

我们想要达到的效果是要在页面下面在加上留言的数据。我们可以直接在前端页面上进行修改然后再在VScode中进行修改。

①选中父元素。

 

②创建一个新的div标签。

③将新的标签赋值给rowDiv。

 ④加入数据。

⑤给rowDiv起一个class属性名。

 

⑥将新加的div标签挂到父标签下面。

 

这样我们就可以在下面加上数据了。那么接下来我们就需要在前端代码中按照上述的步骤进行改进。

代码展示:

前端代码的展示:

<!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><!-- 引入 jquery --><script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script><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"><button id="revert">撤销</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. 构造新 divlet 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. 通过 ajax 构造 post 请求, 把这个新的消息提交给服务器. let body = {"from": from,"to": to,"message": msg};$.ajax({type: 'post',url: 'message',contentType: "application/json;charset=utf8",data: JSON.stringify(body),success: function(body) {// 这是响应成功返回之后, 要调用的回调. console.log("消息发送给服务器成功!");}});}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]);}// 在页面加载的时候, 希望能够从服务器获取到所有的消息, 并显示在网页中. $.ajax({type: 'get',url: 'message',  // url 都是使用相对路径的写法. 相对路径意味着工作路径就是当前文件所在的路径. // 当前文件所在路径是 /message_wall/ , 因此此时构造的请求就是 /message_wall/messagesuccess: function(body) {// body 是收到的响应的正文部分. 如我们之前的约定, body 应该是 json 数组// 由于响应的 Content-Type 是 application/json, 此时收到的 body 会被 jquery 自动的把它从 字符串 // 转成 js 对象数组. 此处就不需要手动的进行 JSON.parse 了. // 此处的 body 已经是一个 JSON.parse 之后得到的 js 对象数组了. // 就需要遍历这个 body 数组, 取出每个元素, 再依据这样的元素构造出 html 标签, 并添加到页面上. let container = document.querySelector('.container');for (let message of body) {let rowDiv = document.createElement('div');rowDiv.className = "row";rowDiv.innerHTML = message.from + " 对 " + message.to + " 说: " + message.message;container.appendChild(rowDiv);}}});</script>
</body>
</html>

后端代码的改进:

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;class Message {public String from;public String to;public String message;@Overridepublic String toString() {return "Message{" +"from='" + from + '\'' +", to='" + to + '\'' +", message='" + message + '\'' +'}';}
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();private List<Message> messageList = new ArrayList<>();//通过这个方法来"获取所有留言消息"@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//需要返回一个json字符串数组,jackson直接可以帮助我们来进行转换String respString = objectMapper.writeValueAsString(messageList);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respString);}//通过这个方法来实现"提交新消息"@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Message message = objectMapper.readValue(req.getInputStream(),Message.class);messageList.add(message);System.out.println("消息提交成功!message=" + message);//响应只是返回200报文,body为空,此时不需要额外处理,默认就是返回200}
}

结果展示:

2.5数据的持久化

上述我们只是实现了在前端页面中在页面重启的时候数据的持久化以及可以让别人也访问到我们的页面,但是如果我们重启服务器的话数据还在吗?答案是不在了,我们的服务器保存的数据是在一个ArrayList中(内存)所以只要进程重启,内存数据可定就没有了,那么我们该怎么将数据进行持久化呢?下面有两种方法。

  • 写到文件中。
  • 写到数据库中。

但是我们一般是将数据写到数据库中来进行数据的持久化。所以接下来我们就使用数据库来进行数据的持久化操作吧。

2.5.1引入JDBC依赖

在maven中找到下面的maven直接引入到pom.xml中。

  

 

2.5.2创建数据库

 

 

2.5.3编写数据库代码

四步走:

  • DataSource
  • Connection
  • PreparedStatement
  • ResultSet

代码展示:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import javax.management.loading.MLet;
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;@Overridepublic String toString() {return "Message{" +"from='" + from + '\'' +", to='" + to + '\'' +", message='" + message + '\'' +'}';}
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();
//    private List<Message> messageList = new ArrayList<>();//通过这个方法来"获取所有留言消息"@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//需要返回一个json字符串数组,jackson直接可以帮助我们来进行转换List<Message> messageList = load();String respString = objectMapper.writeValueAsString(messageList);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respString);}//通过这个方法来实现"提交新消息"@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Message message = objectMapper.readValue(req.getInputStream(),Message.class);save(message);System.out.println("消息提交成功!message=" + message);//响应只是返回200报文,body为空,此时不需要额外处理,默认就是返回200}//这个方法用来往数据库中存储一条记录private void save(Message message) {DataSource dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java107?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("123456");try {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();} catch (SQLException e) {e.printStackTrace();}}//这个方法用来从数据库中查询所有记录private List<Message> load() {//用来承装查询到的数据List<Message> messageList = new ArrayList<>();DataSource dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java107?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("123456");try {Connection connection = dataSource.getConnection();String sql = "select * from message";PreparedStatement statement = connection.prepareStatement(sql);ResultSet resultSet = statement.executeQuery();//按照格式进行解析,然后将取到的数据放入到message中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);}resultSet.close();statement.close();connection.close();} catch (SQLException e) {e.printStackTrace();}return messageList;}
}


结果展示:

 

 

这样就实现了将数据持久化。即使你重新启动服务器或者是重开机数据也都会存在。

3.Cookie回忆

Cookie是浏览器在本地存储数据的一种机制。

①Cookie从哪里来:Cookie是从服务器来的,服务器在响应中会带有Set-Cookie字段,通过这个字段就可以把要保存在浏览器本地的数据给返回回去。

②Cookie到哪里去:后续浏览器在访问服务器的时候,就会把本地所有Cookie都通过http请求给带过去。

③Cookie的作用:其中一种典型的应用就是使用Cookie保存当前用户登录状态。

 在Cookie保存用户身份标,这样的应用场景中,此时身份标识如何分配以及身份信息如何存储,都是需要服务器的支持。其中Session就是服务器这边用来实现用户身份区分的一种机制。它通常是和Cookie配合使用的,它是给当前用户先分配一个SessionId同时记录下当前用户的一些身份信息,这样sessionId就会被返回到浏览器的cookie中后续浏览器访问服务器都会带着这个sessionId从而让服务器识别当前用户身份。

4.Session机制

4.1getSession

通过上述的讲解相信大家对session有了一定的了解,接下来我们就来具体看一下在Servlet中针对cookie和session提供了哪些支持!

HttpServletRequest类中相关的方法:

方法描述
HttpSession getSession()在服务器中获取会话,参数如果为true,则当不存在会话时创建会话,参数如果为false,则当不存在会话时返回null。
Cookie[] getCookies()返回一个数组,包含客户端发送请求的所有Cookie对象,会自动把Cookie中的格式解析成键值对。

getSession()有一个参数是boolean,如果是false,getSession的行为就是:

  1. 读取请求中Cookie里sessionId
  2. 在服务器这边根据sessionId来查询对应session对象。
  3. 如果查到了,就会直接返回这个session对象,如果没有查到,返回null。

如果参数是true,getSession的行为是:

  1. 读取请求中Cookie里sessionId
  2. 在服务器这边根据sessionId来查询对应session对象。
  3. 如果查到了,就会直接返回这个session对象。
  4. 如果没有查到就会创建一个Session对象,同时说生成一个sessionId,以sessionId为key,Session对象为value,把这个键值对存储到服务器里的一个hash表中,同时把sessionId以Set-Cooki的方式返回给浏览器。

下面我们通过模拟实现登录功能来实践一下。

4.2模拟实现登录功能

首先我们提供两个页面:

  1. 登录页(包含两个输入框,输入用户密码,还有一个登录按钮)点击登录按钮,就会发起一个http请求。当服务器处理这个请求的时候就会验证用户名和密码。如果用户名密码OK,就会跳转到主页。
  2. 主页,这里只是单纯的显示当前用户的用户名(欢迎XXX)。

4.2.1登录页的核心代码。

代码展示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登录页面</title>
</head>
<body><form action="login" method="post"><input type="text" name="username"><input type="password" name="password"><input type="submit" value="登录"></form>
</body>
</html>

结果展示:

其中form就会组织这里的数据以键值对的形式提交给服务器,其中key就是input中的name属性,value就是input用户输入的内容,最终会构成post请求,在body里以键值对(类似于query string)的格式进行组织。

4.2.2编写登录页面的Servlet

登录的请求如下所示:

一般像登录页面这样的请求都是POST比较多,这是属于习惯的用法。

 

代码展示:

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.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.先从请求中拿到用户名和密码//为了保证读出来的参数也能支持中文,要记得设置请求的编码方式是utf8req.setCharacterEncoding("utf8");String username = req.getParameter("username");String password = req.getParameter("password");//2.验证用户名和密码是否正确if (username == null || password == null || username.equals("") || password.equals("")) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前输入的用户名和密码不能为空!");return;}//此处假定用户名只能是zhangsan或者是lisi,密码都是123//正常的登录逻辑,验证用户名密码都是从数据库读取的。if (!username.equals("zhangsan") && !username.equals("lisi")) {//用户名有问题resp.setContentType("text/html;charset=utf8");resp.getWriter().write("用户名或密码有误!");return;}if (!password.equals("123")) {//密码有问题resp.setContentType("text/html;charset=utf8");resp.getWriter().write("用户名或密码有误!");return;}//3.用户名和密码验证ok,接下来就创建一个会话。//当前用户处于未登录状态,此时请求的cookie中没有sessionId//此处的getSession是无法从服务器的哈希表中找到该session对象的。//由于此处把参数设置为true了,所以就允许getSession在查询不到的时候,创建新的session对象和sessionId//并且会自动把这个sessionId和session对象存储在哈希表中。//同时返回这个session对象,并且在接下来的响应中会自动把这个sessionId返回给客户端浏览器。HttpSession session = req.getSession(true);//接下来可以让刚刚创建好的session对象存储咱们定义的数据。就可以在这个对象中存储用户的身份信息。session.setAttribute("username",username);//4.登录成功之后,自动跳转到主页resp.sendRedirect("index");}
}

4.2.3编写生成主页的Servlet

 

代码展示:

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.servlet.http.HttpSession;
import java.io.IOException;
//这个Servlet用来动态生成主页面
@WebServlet("/index")
public class IndexServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//此处禁止创建会话,如果没有找到,认为用户是未登录的状态//如果找到了才认为是登录状态。HttpSession session = req.getSession(false);if (session == null) {//未登录状态resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前用户为登录!");return;}String username = (String) session.getAttribute("username");if (username == null) {//虽然有会话对象,但是里面没有必要的属性,也认为是登录状态异常resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前用户未登录!");return;}//如果上述检查都OK,接下来就直接生产一个动态页面resp.setContentType("text/html;charset=utf8");resp.getWriter().write("欢迎你!" + username);}
}

4.2.4起启动浏览器

启动浏览器之后我们就可以看到下面的内容了。

 

同样我们也可以借助fiddler来查看发送的内容。

上述的sessionId也不会一致存储下去,比如服务器重新启动原来的hash表中内容就会消失不见,此时再次访问的时候就可能会出现sessionId无法查询到,于是就会被识别成未登录状态。但是这里我们使用了Smart Tomcat,他为了方便程序猿调试程序,会在停止的服务器的时候,会把会话持久化保存,并且在下次启动的时候,自动那个把会话恢复到内存中,这个时候会话时不会丢失的。上述这一套是否生效,这取决于你使用的Smart Tomcat的版本。

结束语:

好了这节小编就给大分享到这里啦,希望这节对大家学习JavaEE初阶有一定帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ELK高级搜索(二)

文章目录 7&#xff0e;Java api 文档管理7.1 es技术特点7.2 获取数据7.3 文档查询7.4 文档新增7.5 文档修改7.6 文档删除7.7 文档bulk 8&#xff0e;图解es内部机制8.1 es分布式基础8.2 分片shard、副本replica8.3 单node环境创建index8.4 多node环境replica shard8.5 横向扩容…

Quartus II安装下载驱动

Quartus II安装下载驱动 安装步骤&#xff1a; &#xff08;1&#xff09;安装完quartus后会自带USB-Blaster的驱动程序&#xff0c;因此不用再去网上下载。 &#xff08;2&#xff09;右键点脑左下角win&#xff0c;找到设备管理器并进入。 &#xff08;3&#xff09;找到…

Python爬虫 异步、缓存技巧

在进行大规模数据抓取时&#xff0c;Python爬虫的速度和效率是至关重要的。本文将介绍如何通过异步请求、缓存和代理池等技巧来优化Python爬虫的速度和性能。我们提供了实用的方案和代码示例&#xff0c;帮助你加速数据抓取过程&#xff0c;提高爬虫的效率。 使用异步请求、缓…

多店铺智能客服,助力店铺销量倍增

近年来电商发展得非常快速&#xff0c;市场竞争也是愈发激烈了。商家不仅需要提高产品和服务的质量&#xff0c;还要争取为自己获取更多的曝光&#xff0c;以此来分散运营的风险和降低经营的成本&#xff0c;所以越来越多的商家也开始转向多平台多店铺运营。但即使运营多个平台…

Python-pyqt不同窗口数据传输【使用静态函数】

文章目录 前言程序1&#xff1a;caogao1.py输入数据界面程序2&#xff1a;caogao2.py接收数据界面 程序3 &#xff1a;将输入数据界面和接收数据界面组合成一个总界面讲解 总结 前言 在编写pyqt 页面时有时候需要不同页面进行数据传输。本文讲解静态函数方法。直接看示例。 程…

AntDesign 自定义图片上传前端压缩画质

为什么压缩图片&#xff1f; 应为现在公司没有使用云数据库 从而为了减少服务器的消耗需要将用户上传的图片压缩 前端压缩图片的技术选择&#xff01; 查阅资料发现当下两种压缩的方法&#xff01;&#xff01;第一种使用工具库实现 npm install image-conversion --save 第…

【Linux】序列化与反序列化

目录 前言 什么是应用层&#xff1f; 再谈"协议" 什么是序列化和反序列化 网络版计算器 整体流程实现 Sock.hpp的实现 TcpServer.hpp的实现 Protocol.hpp的实现 CalServer.cc的编写 CalClient.cc的编写 整体代码 前言 本章是属于TCP/UDP四层模型中的第一层…

【NCRE 二级Java语言程序设计01】全国计算机等级考试初识

目录 前言一、简单认识全国计算机等级考试1.官方介绍2.省级和全国级3.考试内容 二、NCRE正确入口三、官方重要资源分布1.大纲教材2.相关下载3.试题选登4.常见问题 总结 前言 &#x1f4dc; 本专栏主要是分享自己备考全国计算机二级所学、所搜集的资料。虽然有 Java 相关基础&am…

【爬虫GUI】YouTube评论采集软件,突破反爬,可无限爬取!

文章目录 一、背景介绍1.1 软件说明1.2 效果演示 二、科普知识2.1 关于视频id2.2 关于评论时间 三、爬虫代码3.1 界面模块3.2 爬虫模块3.3 日志模块 四、获取源码及软件 一、背景介绍 你好&#xff0c;我是马哥python说 &#xff0c;一名10年程序猿。 最近我用python开发了一…

Spring-SpringBoot-SpringMVC-MyBatis常见面试题

文章目录 Spring篇springbean是安全的的?什么是AOP你们工作中有用过AOP吗spring中的事务是如何实现的spring中事务失效场景Spring的生命周期spring中的循坏依赖springMVC的执行流程springboot的启动原理常用注解MyBatis执行流程Mybatis是否支持延迟加载&#xff1f;Mybatis的一…

ICCV 2023 | 小鹏汽车纽约石溪:局部上下文感知主动域自适应LADA

摘要 主动域自适应&#xff08;ADA&#xff09;通过查询少量选定的目标域样本的标签&#xff0c;以帮助模型从源域迁移到目标域。查询数据的局部上下文信息非常重要&#xff0c;特别是在域间差异较大的情况下&#xff0c;然而现有的ADA方法尚未充分探索这一点。在本文中&#…

网络安全02-C段扫描、开放端口

查询网站IP https://seo.chinaz.com/hetianlab.com 扫描指定IP&#xff1a;例&#xff1a;nmap -A -T4 ww.hetianlab.com -oX out.html 扫描指定段&#xff1a;例&#xff1a;nmap -O -Pn -A 192.168.113.1-200 扫描整个C段&#xff1a;例&#xff1a;nmap -O -Pn -A 192.168.…

SpringBoot+MyBatisPlus+MySql+vue2+elementUi的案例、java访问数据库服务、java提供接口服务

文章目录 前言后端关键代码前端关键代码完整代码 前言 1、项目不使用前后端分离。 2、在创建SpringBoot的时候要注意各个插件间的版本问题。 3、后端技术SpringBootMyBatisPlusMySql。 4、前端技术vue2elementUi。 后端关键代码 简单介绍 1、数据库名称ssm_db 2、表名称tbl_bo…

C++自创题目——第一期

一、题目描述&#xff1a; 在一段时间内&#xff0c;到达港口的船有n艘&#xff0c;其中每艘船的信息包括:到达时间t(表示第t秒)&#xff0c;船上乘客数k&#xff0c;以及k名乘客的国籍。输出前3600s内每艘船上国籍种数&#xff0c;并输出国籍种数最少的船只的到达时间。 二、…

数字货币量化交易平台

数字货币量化交易平台是近年来金融科技领域迅速崛起的一种创新型交易方式。它通过应用数学模型和算法策略&#xff0c;实现对数字货币市场的自动交易和风险控制。然而&#xff0c;要在这个竞争激烈的领域中脱颖而出&#xff0c;一个数字货币量化交易平台需要具备足够的专业性&a…

服务器数据恢复-AIX PV完整镜像方法以及误删LV的数据恢复方案

AIX中的PV相当于物理磁盘&#xff08;针对于存储来说&#xff0c;PV相当于存储映射过来的卷&#xff1b;针对操作系统来说&#xff0c;PV相当于物理硬盘&#xff09;&#xff0c;若干个PV组成一个VG&#xff0c;AIX可以将容量不同的存储空间组合起来统一分配。AIX把同一个VG的所…

git 基础

1.下载安装Git&#xff08;略&#xff09; 2.打开git bash窗口 3.查看版本号、设置用户名和邮箱 用户名和邮箱可以随意起&#xff0c;与GitHub的账号邮箱没有关系 4.初始化git 在D盘中新建gitspace文件夹&#xff0c;并在该目录下打开git bash窗口 git init 初始化完成后会…

小区物业业主管理信息系统设计的设计与实现(论文+源码)_kaic

摘 要 随着互联网的发展&#xff0c;网络技术的发展变得极其重要&#xff0c;所以依靠计算机处理业务成为了一种社会普遍的现状。管理方式也自然而然的向着现代化技术方向而改变&#xff0c;所以纯人工管理方式在越来越完善的现代化管理技术的比较之下也就显得过于繁琐&#x…

【大数据】Doris:基于 MPP 架构的高性能实时分析型数据库

Doris&#xff1a;基于 MPP 架构的高性能实时分析型数据库 1.Doris 介绍 Apache Doris 是一个基于 MPP&#xff08;Massively Parallel Processing&#xff0c;大规模并行处理&#xff09;架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff…