Servlet

1.Servlet是什么

Servlet是一种实现动态页面的技术。是一组Tomcat提供给程序员的API,帮助程序员简单高效的开发一个web app

回顾 动态页面 VS 静态页面

静态页面也就是内容固定的页面,即使 用户不同/时间不同/输入参数不同,页面的内容也不会发生改变。(除非网站的开发人员修改源代码)

对应的,动态页面指的是 用户不同/时间不同/输出的参数不同,页面也会跟着改变。

【举一个栗子】

Tomcat的主页就是一个静态页面,什么时候打开,什么人打开都是相同的页面。

 而B站的页面是一个动态的页面,根据不同的时间、不同的地区、不同的人员推送不同的内容

构建动态页面的技术有很多,每种语言都有一些相关的库/框架来做这些事情。

Servlet就是Tomcat这个HTTP服务器提供给Java的一组API,来完成构建动态页面这个任务。

Servlet主要做的工作

  • 允许程序员注册一个类,在Tomcat收到某个特定的HTTP请求的时候,执行类中的一些代码;
  • 帮助程序猿解析HTTP请求,把HTTP请求从一个字符串解析成一个HttpRequest对象;
  • 帮助程序员构造HTTP响应。程序员只要给指定的HttpResponse对象填写一些属性字段,Servlet就会自动的安装HTTP 协议的方式构造出一个HTTP响应字符串,并通过Socket写回客户端。

简而言之,Servlet是一组Tomcat提供的API,让程序员自己写的代码能很好的和Tomcat配合起来,从而更简单的实现一个web app

2.第一个Servlet程序

2.1创建项目

打开IDEA -> 创建maven文件 -> 设置路径,给文件命名 -> 完成创建

这个是创建成功后的样子。

2.2引入依赖

Maven项目创建完毕后,会自动生成一个pom.xml文件(如上图)

我们需要在pom.xml种引入Servlet API依赖的jar包

  1. 在Maven中央仓库中搜索 "servlet",一般第一个结果就是。
  2. 选择版本。我们一般使用3.1.0版本

    Servlet的版本要和tomcat相匹配,我们使用的是Tomcat8.5,那么就需要使用Servlet 3.1.0。我们可以查看 http://tomcat.apache.org/whichversion.htm 查询版本之间的对应关系。
  3. 把中央仓库中提供的xml复制到项目的pom.xml中。

    格式如下:

 【注意】如果我们发现粘贴上去,一些内容出现红色或者是出现报错,我们刷新一下,这里IDEA会自动下载这些资源。

关于groupId, artifactId, version

这几个东西我们不必关注。如果我们要把这个写的代码发布到中央仓库上,那么我们就要设定好这几个ID

groupId:表示组织名称

artifactId:表示项目名称

version:表示版本号

中央仓库就是按照这三个字段来确定唯一一个包(下面红色框里面的就是)

 2.3创建目录

当项目创建好了以后,IDEA会帮我们自动创建出一些目录。形如

 这些目录中:

  • src 表示源代码所在的目录
  • main/java 表示源代码的根目录。后续创建 .java 就放到这个目录中
  • main/resources 表示项目的一些资源文件所在的目录
  • test/java 表示测试代码的根目录
  • 同时目录不够,我们还需要一些新的目录

1)创建相关目录

在main目录下,和java目录并列,创建一个webapp目录(注意:不是webapps),同时在里面创建WEB-INF目录,同时在此目录下创建web.xml文件,如下图(文件的名字不要更改,基本上是固定格式)

 2)编写web.xml

往web.xml中拷贝以下代码,我们在这里并不要关注细节(注意:下面代码第三行,可能会报错,这是IDEA不是识别的问题,我们不必理会)

<!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>

webapp目录就是未来部署到Tomcat中的一个重要目录,当前我们往webapp中放一些静态资源,比如html,css等

在这个目录中国还有一个重要的文件web.xml。Tomcat找到这个文件才能正确处理webapp中的动态资源

2.4编写代码

在Java目录中创建一个类HelloServlet,代码如下:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {System.out.println("hello");resp.getWriter().write("hello");}
}
  • 创建一个类HelloServlet,继承自HttpServlet
  • 在这个类的上方加上 @WebServlet("/hello") 注解,表示Tomcat收到的请求,路径为 /hello 的请求才会被调用HelloServlet这个类的代码
  • 重写 doGet 方法。doGet的参数有两个,分别表示 收到的HTTP的请求 和 要构造的HTTP响应。这个方法会在Tomcat收到GET请求时触发。
  • HttpServletRequest表示HTTP请求。Tomcat按照HTTP请求的格式把字符串格式的请求转化成一个HttpServletRequest对象。后续我们从此对象可以获取请求中的相关信息。
  • HttpServletResponse表示HTTP响应。代码中把响应对象构造好
  • response.getWriter()会获取一个流对象,通过这个流对象就可以写入一些数据,写入的数据会被放在HTTP响应的body部分。

额外信息介绍

  1. 我们的代码不是通过main方法作为入口的。main方法包含在Tomcat里,我们写的代码会被Tomcat在合适的时机调用
  2. 我们随便写个类都能被Tomcat调用吗?满足啥样的条件才能被调用?
  • 创建的类需要继承自HttpServlet
  • 这个类需要使用@WebServlet注解关联上一个HTTP的路径
  • 这个类需要实现 doXXX 方法        

当这三个条件满足的时候,Tomcat就可以找到这个类,并在合适的时机进行调用。

2.5打包程序

使用maven进行打包,打开maven窗口(一般在IDEA右侧就能看到Maven窗口,如果看不到,点击菜单 -> View -> Tool Window -> Maven打开)

然后展开Lifecycle,双击package即可进行打包

 打包成功后,可以看到target目录下,会生成一个jar包

这样的jar包并不是我们需要的,Tomcat需要识别的是另一种war包格式(可以理解为一种不同格式的压缩包),同时这个jar包的名字太复杂了,我们也希望这个名字能更简单一点

【war包和jar包的不同区别】

jar包是普通的Java程序打包后的结果,里面会包含一些 .class 文件

war包是 java web 的程序,里面除了会包含 .class 文件之外,还会包含HTML,CSS,JS,图片,以及其他的jar包,打成 war 包格式才能被Tomcat识别

在 pom.xml 中新增一个packing标签,表示打包的方式是打成一个 war 包,同时我们再添加一个build标签,内置了一个finalName标签,表示打出的war包的名字是HelloServlet

 我们重新打包即可

2.6部署程序

把war拷贝到Tomcat的webapps目录下,启动Tomcat,Tomcat会自动把war包解压

2.7验证程序

此时通过浏览器访问 127.0.0.1:8080/ServletHelloworld/hello 就能看到响应的结果了

 【注意】URL中的PATH分成两个部分,其中 ServletHelloworld 是 Context Path,hello为 Servlet Path

 

 3.更方便的部署

手动拷贝到war包到Tomcat的过程比较的麻烦,我们可以使用更加简单的方式。

此处我们使用IDEA中的Smart Tomcat插件完成这个工作。

什么是插件?

像IDEA这样的程序软件非常的强大,但是也是无法做到面面俱到。对于一些特殊场景的功能,比如在开发的时候,我们要求自动播放音乐或者能获得鼓励的语音,如果我们需要,就能自己单独安装。

插件就是对程序的一些特定场景,做出一些特定的功能的扩展。

3.1安装Smart Tomcat插件

1.菜单 -> 文件 -> Setting

 2.选择Plugins,选择Marketplace,搜索"tomcat",点击"Install"

 3.安装完成后,重新启动

3.2配置Smart Tomcat插件

1.点击页面右上角的”Add Configuration"

 2.选择左侧"Smart Tomcat"

 3.在Name这一栏填写一个名字(可以随便写)

在Tomcat Server这一栏选择Tomcat所在的目录。其他选项不用修改

 

 4.点击OK之后,右上角变成了这样(如下图所示),我们点击绿色的三角,IDEA就会自动进行编译,部署,启动Tomcat的过程

5.访问页面

在浏览器中输入 127.0.0.1:8080/ServletHelloWorld/hello  访问页面

使用此插件的好处:

我们不需要打包和部署,直接点击运行即可,这方便了在开发过程中让项目跑起来的过程。

4.Servlet API详解

4.1HttpServlet

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

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

在实际的开发过程中,我们重点写的是doXXX方法,很少会重写init/destory/service

这些方法的调用时机,就称为“Servlet生命周期”(描述了Servlet从创建到销毁的全过程)

 【注意】HttpServlet的实例只是在程序启动的时候创建一次,而不是在每次收到HTTP请求的时候创建。

1.代码示例:处理GET请求

创建MethodServler.java,创建doGet方法

@WebServlet("/method")
public class MethodServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {resp.getWriter().write("get method");}
}

我们访问构造一个请求对其进行访问(注意路径问题,不要输入错误)

关于乱码问题

我们如果在响应中输入中文,例如:

resp.getWriter().write("GET 方法");

我们再次访问浏览器,看看能否正常访问

 关于“乱码”

中文的编码方式有很多,其中最常见的就是UTF-8

如果没有显示的指定编码方式,则浏览器不能正确识别,就会出现乱码的情况。

我们在代码中,通过 resp.setContentType("test/html; charset=utf8");显示的指定编码方式

 2.代码示例:处理POST请求

在MethodServlet.java中,新增doPost方法。

@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("Post 方法");}

我们构造POST请求,看看有啥效果(注意每次写完新的代码要重启服务器)

 4.2HttpServletRequest

当Tomcat通过Socket API读取HTTP请求(字符串),并按照HTTP协议的格式把字符串解析成HttpServletRequest对象

核心方法】

方法描述
String getProtocol()返回请求协议的名称和版本
String getMethod()返回请求的HTTP方法的名称
String getRequestURI()从协议名称直到HTTP请求的第一行的查询字符串中,返回该请求的URI的一部分
String getContextPath()返回请示上下文的请求URI部分
String getQueryString()返回包含在路径后的请求URL中的查询字符串

Enumeration

getParameterName()

返回一个String对象的枚举,包含在该请求中包含的参数的名称
String getParameter(String name)以字符串的形式返回请求参数的值,如果不存在返回NULL
String[] getParameterValues(String name)返回一个字符串对象的数组,包含所有给定参数的值,如果参数不存在返回NULL
String getHeader(String name)以字符串的形式返回请求头的值
String getCharacterEncoding()请求返回主体中使用的字符编码的名称
String getContentType()返回请求主体中MIME类型,如果不知道类型返回NULL
int getContentLength()以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知返回 -1
InputStream getInputStream()用于读取请求的body内容,返回一个InputStream对象

1.代码示例:打印请求信息

创建ShowRequest类

@WebServlet("/ShowRequest")
public class ShowRequest extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {StringBuilder ret = new StringBuilder();ret.append(req.getProtocol());//获取协议名称和版本号ret.append("<br>");//换行标签ret.append(req.getMethod());//获取方法名ret.append("<br>");ret.append(req.getRequestURI());//获取URIret.append("<br>");ret.append(req.getContextPath());//获取上下文路径ret.append("<br>");ret.append(req.getQueryString());//获取查询字符串ret.append("<br>");//获取中参数名称和参数的值Enumeration<String> headerNames = req.getHeaderNames();while(headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();ret.append(headerName + " : " + req.getHeader(headerName));ret.append("<br>");}resp.getWriter().write(ret.toString());}
}

 2.代码示例:获取GET请求中的参数

GET请求种的传输一般都是通过query string传递给服务器的,形如

创建GetParameter类

@WebServlet("/getParameter")
public class getParameter extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {resp.setContentType("text/html; charset=utf8");String useId = req.getParameter("useId");String classId = req.getParameter("classId");resp.getWriter().write(useId + " : " + classId);}
}

 3.代码示例:获取POST请求中的参数(1)

POST请求的参数一般通过body传递给服务器。body种的数据格式有很多种。如果我们采取form表单的形式,仍然可以通过getParameter获取参数的值

创建PostParameter

@WebServlet("/postParameter")
public class PostParameter extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {resp.setContentType("text/html; charset=utf-8");String userId = req.getParameter("userId");String classId = req.getParameter("classId");resp.getWriter().write(userId + " : " + classId);System.out.println(userId + " : " + classId);}
}

 4.代码示例:获取POST请求中的参数(2)

如果POST请求中的body是按照JSON的格式来传递,那么获取参数的代码也要发生调整。

首先我们要引入依赖:Maven中央仓库搜索 jackson (一般第一个就是我们需要的),然后随便选一个版本,我们复制相关的代码。(我们复制过去后,可能刷新才能开始下载)

 然后我们开始编写代码

  •  我们创建一个User,用于表示json传过来的每个信息;
  • 然后创建一个objectMapper对象,用于等会将json转化为Java对象
  • 我们将json转化为Java对象,并在控制台输出,然后resp返回OK即可。
//用于储存json传过来的内容
class User {public String username;public String password;
}@WebServlet("/json")
public class JsonServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {//将json转化一个Java对象User user = objectMapper.readValue(req.getInputStream(), User.class);//我们返回OKSystem.out.println(user.username + " : " + user.password);resp.getWriter().write("OK");}
}

JSON的格式:

  • 每个元素使用{ }
  • 多个元素之间使用 ”," 分割
  • 所有元素外面使用 [ ] 括起来,表示数组。
  • 元素内,键与值之间使用 :分割,左边是键,右边是值,同时用 " " 引起来

4.3HttpServletResponse

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

核心方法

 方法描述
void setStatus(int c)为响应设置一个状态码
void setHeader(String name, String value)设置一个带有给定的名称和值的header.如果name已经存在,则会覆盖旧的值。
void addHeader(String name, String value)添加一个带有给定的名称和值的header.如果name已经存在,不会覆盖旧的值,并列添加新的键值对
void setContentType(String type)设置被发送到客户端的响应的内容类型
void setCharacterEncoding(String charset)设置被发送到客户端的响应的字符编码方式
void sendRedirect(String location)使用指定的重定向位置URL发送临时重定向相应到客户端
PrintWriter getWriter()用于往body中写入文本格式数据
OutputStream getOutputStream()用于往body中写入二进制格式数据

注意:对于状态码/响应头的设置要放到 getWriter / getOutputStream 之前,否则可能会设置失败

1.代码示例:设置状态码

设置一个程序,用户在浏览器通过参数指定要返回的状态码。

@WebServlet("/statusServlet")
public class StatusServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {String statusString = req.getParameter("status");if(statusString != null) {resp.setStatus(Integer.parseInt(statusString));}resp.getWriter().write(statusString);}
}

2.代码示例:自动刷新 

实现一个程序,让浏览器每秒钟自动刷新一次,并且显示当前的时间戳

@WebServlet("/autoRefreshServlet")
public class AutoRefreshServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {resp.setHeader("Refresh", "1");resp.getWriter().write("time : " + System.currentTimeMillis());}
}

 3.代码重定向

实现一个程序,返回一个重定向HTTP响应,自动跳转到另外的一个页面

@WebServlet("/redirectServlet")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {resp.sendRedirect("http://www.sogou.com");}
}

5.代码示例:服务器版表白墙

5.1准备工作

  1. 创建maven项目
  2. 创建必要的目录:webapp -> WEB-INF -> 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

引入依赖(servlet,mysql,jackson),配置生成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>ConfessionWall</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/com.fasterxml.jackson.core/jackson-databind --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.0</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>5.1.49</version></dependency></dependencies><packaging>war</packaging><build><finalName>ConfessionWall</finalName></build></project>

4.把表白墙前端页面拷贝到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><!-- 引入 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>

5.2约定前后端交互接口

“前后端交互接口”是进行Web开发中的关键环节。

具体来说,就是允许页面给服务器发送哪些HTTP请求,并且每种请求预期获取什么样的HTTP响应

1.创建数据库相关表格表

create table Message(id int unique auto_increment, 
`from` varchar(20),
`to` varchar(20), 
message text);

2.查询所有表白墙的信息

  • 创建表白墙信息类,表示谁对谁说什么
  • 查询原来的数据中的信息并返回list
  • 把这个list传入resp
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();List<Message> messageList = new ArrayList<>();//加载已经保存到数据库中的信息@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {//通过list来保存相关信息,通过load来从数据库调用信息messageList = new ArrayList<>();//防止每次刷新时,messageList中有的信息与数据库中的重复导致再次往里面存储messageList = load();String respString = objectMapper.writeValueAsString(messageList);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respString);}
//从数据库中读信息private List<Message> load(){DataSource dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("1020118096");try {Connection connection = dataSource.getConnection();String sql = "select * from message";PreparedStatement statement = connection.prepareStatement(sql);ResultSet 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");messageList.add(message);}resultSet.close();statement.close();connection.close();} catch (SQLException e) {e.printStackTrace();}return messageList;}
}

 3.提交一条信息

    @Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {Message message = objectMapper.readValue(req.getInputStream(), Message.class);save(message);}private void save(Message message) {DataSource dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("1020118096");try {Connection connection = dataSource.getConnection();String sql = "insert into message(`from`, `to`, 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();}}

4.输入响应的网站进行测试

在搜索框中输入响应的URL:表白墙http://127.0.0.1:8080/ConfessionWall/ConfessionWall.html

 我们写如响应的信息并提交:

 我们刷新页面:

 关闭服务器,并重启后打开页面信息也不会丢失。

6.Cookie和Session

6.1回顾Cookie

HTTP协议自身是属于“无状态”协议。

“无状态”的含义是:

默认情况下HTTP协议的客户端和服务器之间的这次通信,和下次通信之间是没有联系的。

【栗子】有直接联系的通信:比如一个后台管理系统(包含登录页面和管理页面),我们正常情况下,我们要先打开登录页面进行登录,才能进入管理页面。我们如果直接输入管理页面的URL,我们理论上是不允许的。这时进行的就是有联系的通信。

 此时服务器这边就需要记录令牌信息,以及令牌对应的用户信息,这个就是Session机制所作的工作。

 6.2理解会话机制(Session)

什么是“令牌”?令牌就是规定一个特殊的字符串,这个字符串对应着每个用户的详细信息,以便后续操作不需要再次登录。

会话的本质是一个“哈希表”,储存了一些键值对。key就是令牌的ID(token/sessionID),value就是用户信息。

sessionID就是服务器生成的一个“唯一性字符串”,从session机制的角度来看,这个唯一性字符串称为“sessionId"。但是站在整个登录流程看,也可以把这个唯一性字符串称为“token"。本质上是一个东西的不同叫法。

 【登录的全过程】

  • 用户打卡登录页面,输入用户名和密码进行提交。
  • 服务器收到这些信息,与数据库中的信息进行匹配,匹配成功,服务器中新增一条新的记录,并返回响应的结果和sessionId给客户端。
  • 客户端进入登录页面,并返回其他响应的页面或者重新打开登录页面,客户端会把响应的sessionId一并提交过去,从而达到不用反复登录的目的。

Servlet的Session默认是保存到内存中的。如果重启服务器则Session数据会丢失。

6.3Cookie和Session的区别

  • Cookie是客户端机制(主要保存到客户端主机)。Session是服务器端的机制(主要保存到服务器端)
  • Cookie和Session经常会一起配合来使用,但不是必须配合。
    • 完全可以使用Cookie来保存一些数据在客户端。这些数据不一定是用户的身份信息,也不一定是token/sessionId,可以是一些用户自定义设置或主题等。

6.4核心方法

HttpServletRequest类中的关于Session或Cookie的方法

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

HttpServletResponse类中的关于Session或Cookie的方法

方法描述
void addCookid(Cookie cookie)把指定的cookie添加到响应中

HttpSession类中的相关方法:

方法描述
Object getAttribute(String name)

该方法返回在该session会话中具有指定名称的对象,如果没有指定名称的对象,则返回null.

void setAttribute(String name, object value)该方法使用指定的名称绑定一个对象到该session会话
boolean isNew()判断当前是否是新创建的会话

Cookie类中的相应的方法

每个Cookie对象就是一个键值对。

方法描述
String getName()该方法返回Cookie的名称,名称在创建后不能改变。(这个值是SetCookie字段设置给浏览器的)
String getValue()该方法获取与cookie关联的值
void setValue(String newValue)该方法设置与cookie关联的值
  • HTTP的Cookie字段中存储的实际上是多组键值对。每个键值对在Servlet中对应了一个Cookie对象
  • 通过HttpServletRequest.getCookies()获取到请求中一系列Cookie键值对。
  • 通过HttpServletResponse.addCookie()可以向响应中添加新的Cookie键值对。

6.5代码示例:实现用户登录

1. 引入相应的依赖

我们引入Servlet API依赖的jar包和JDBC

<?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>Login</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/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency></dependencies>
</project>

2. 创建相应的目录

<!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. 编写相应的前端代码(我们学习的是后端,这里我们复制即可)

<!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><form action="login" method="post"><input type="text" name="username"><input type="password" name="password"><input type="submit" value="登录"></form>
</body>
</html>

 4. 编写相应的后端代码

  • 登录页面提交账号密码所需要的后端代码(用于验证密码的正确性,以及给用户提示):
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;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 javax.sql.DataSource;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** Describe:* User:lenovo* Date:2023-07-01* Time:17:20*/
//这个类用来实现登录时的校验@WebServlet("/login")
public class LoginServletServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws 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;}//3.验证用户名和密码的正确性boolean ret = normal(username, password);if(ret) {System.out.println(ret);// 用户名和密码正确,我们创建相应的session,并存储HttpSession session = req.getSession(true);session.setAttribute("username", username);//4.登录成功后,自动跳转主页resp.sendRedirect("index");}else {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("用户名或密码错误");}}//验证账号密码是否正确private boolean normal(String username, String password) {DataSource dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("1020118096");try {Connection connection = dataSource.getConnection();String sql = "select password from login where username = ?";PreparedStatement statement = connection.prepareStatement(sql);statement.setString(1, username);ResultSet resultSet = statement.executeQuery();while(resultSet.next()) {String ret = resultSet.getString("password");if(ret.equals(password)) {return true;}}} catch (SQLException e) {e.printStackTrace();}return false;}}
  • 用户输入密码后返回的结果:
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;/*** Describe:* User:lenovo* Date:2023-07-02* Time:9:50*/
@WebServlet("/index")
public class IndexServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {// 此处禁止建立会话,如果没有找到,认为用户时未登录状态// 如果找到了才认为是登录状态HttpSession session = req.getSession(false);if(session == null) {System.out.println("Session");// 未登录状态resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前用户未登录!");return;}String username = (String)session.getAttribute("username");if(username == null) {System.out.println("userName");// 虽然有会话对象,但是里面没有必要的属性,也认为是登录异常状态resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前用户未登录!");return;}//上述检查都ok,接下来就直接生成一个动态页面resp.setContentType("text/html; charset=utf8");resp.getWriter().write("欢迎你!" + username);}
}

 

 5.打包和运行(略)

7.上传文件

上传文件也是日常开发中一类常见的需求。在Servlet也是支持的。

7.1核心方法

HttpServletRequest类方法

方法描述
Part getPart(String name)获取请求中给定name的文件
Collection<Part> getParts()获取所有文件

Part类方法

方法描述
String getSubmittedFileName()获取提交文件的名字
String getContentType()获取提交的文件类型
long getSize()获取文件的大小
void write(String path)把提交的文件写入磁盘文件

7.2代码示例

实现代码,通过页面提交一个图片到服务器

1.创建相关目录和文件

 2.引入相应的依赖。

<?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>Upload pictures</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></dependencies></project>

3.创建upload.html,放到webapp目录中

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><form action="upload" enctype="multipart/form-data" method="POST"><input type="file" name="MyImage"><input type="submit" value="提交图片"></form>
</body>

4.创建UploadServlet类

@MultipartConfig
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Part part = req.getPart("MyImage");System.out.println(part.getSubmittedFileName());System.out.println(part.getContentType());System.out.println(part.getSize());part.write("e:/MyImage.jpg");resp.getWriter().write("upload ok");}
}

5.使用插件进行运行。

 

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

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

相关文章

LVS负载均衡群集

目录 企业集群的应用 1、什么是集群 2、集群使用在哪个场景 3、集群分类(三种 )集群类型 负载均衡 高可用 高性能运算 4、负载均衡集群的架构 5、负载均衡集群工作模式 6、LVS虚拟服务器 LVS ipvsadm LVS和nginx比较 7、LVS负载调度算法 8、案例LVS-NAT部署实战 企…

数学建模——曲线拟合

一、曲线拟合简介 1、曲线拟合问题的提法 已知一组数据&#xff08;二维&#xff09;&#xff0c;即平面上n个点 (xi,yi)(i1,2,…,n)&#xff0c; xi互不相同。寻求一个函数yf(x)&#xff0c;使得f(x)在某种准则下与所有的数据点最为接近&#xff0c;即拟合得最好。 2、…

【雕爷学编程】Arduino动手做(149)---MAX9814咪头传感器模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

docker框架02docker的安装

01.这次的docker是在centos版本下的Linux系统中安装的。 02.输入命令 01.先去卸载就得版本 02.安装工具包&#xff0c;和设置镜像仓库 03.由于网络的问题&#xff0c;访问国内的阿里云镜像 修改&#xff1a; 04.更新索引和安装社区版的docker 05.启动docker 06.用命令d…

postman不能进行并发测试

1.按照网上文档的配置 2.在登录接口里睡眠5s&#xff0c;如果是并发的话&#xff0c;所有的请求都会一起睡眠5s 3.测试结果&#xff1a;请求是每隔5s串行执行的

Microsoft遭遇DDoS攻击,3000万客户数据遭窃

6月初&#xff0c;微软部分服务遭遇严重中断&#xff0c;包括Outlook电子邮件、OneDrive文件共享应用程序和云计算基础设施Azure。 一个名为”匿名苏丹”的(又名“风暴-1359”)的组织声称对此次DDoS攻击负责。 匿名苏丹组织自2023年1月以来一直活动频繁&#xff0c;声称其目标…

反向代理缓存服务器 Squid 、Nginx、CDN部署讲解

目录 一、Squid 反向代理1.1 概念1.2 工作机制1.3 搭建 二、 Nginx 反向代理缓存 一、Squid 反向代理 1.1 概念 如果 Squid 反向代理服务器中缓存了该请求的资源&#xff0c;则将该请求的资源直接返回给客户端&#xff1b;否则反向代理服务器将向后台的 Web 服务器请求资源&a…

pytorch 的matmult()函数详解

torch.matmul()也是一种类似于矩阵相乘操作的tensor连乘操作。但是它可以利用python中的广播机制&#xff0c;处理一些维度不同的tensor结构进行相乘操作。 matmul 就是矩阵求 叉乘 如果是二维矩阵&#xff0c;两个矩阵的大小应该为m*n &#xff0c;n*m。 一维向量的乘积&…

数据结构之队列

1.队列的定义 队列(Queue)&#xff1a;也是一种运算受限的特殊线性表。其插入和删除操作分别在线性表的两端进行&#xff08;只允许在表的一端进行插入&#xff0c;而在另一端进行删除&#xff09;。允许删除的一端称为队头(front)&#xff0c;允许插入的一端称为队尾(rear) …

python读取广州-湛江天气csv文件并做可视化仪表盘

1.读取广-湛.csv文件 import pandas as pd data pd.read_csv(广-湛天气.csv) data 2.去除多余字符 #去除多余字符 data[[最高温度,最低温度]] data[[最高温度,最低温度]].apply(lambda x: x.str.replace(,).replace(, 0)) data.head() 3.删除2023年数据,并计算平均温度保存到…

全网最全,Pytest自动化测试框架 assert 断言使用实战,快速通关

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Pytest与Unittest…

如何让小家电带电池设备快速充满电

如今随着这几年的USB-C PD适配器的普及&#xff0c;消费者手上的PD协议适配器越来越普遍&#xff0c;如何让微软surface 充电器线支持使用PD适配器快充呢&#xff1f;不妨加入一颗受电端PD协议取电芯片——LDR6015 就可以打造一根 PD电源线适配pro7/6/5/4/laptop/book/go Type-…