Servlet,session(Tomcat提供的API接口)
servlet概论
servlet是tomcat给我们提供的一组操控HTTP协议的API。
Servlet 就是 Tomcat 这个 HTTP 服务器提供给 Java 的一组 API, 来完成构建动态页面这个任务。
Servlet 主要做的工作:
- 允许注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类中的一些代码.。
- 解析 HTTP 请求, 把 HTTP 请求从一个字符串解析成一个 HttpRequest 对象。
- 构造 HTTP 响应.,只要给指定的 HttpResponse 对象填写一些属性字段, Servlet 就会自动的安装 HTTP
协议的方式构造出一个 HTTP 响应字符串, 并通过 Socket 写回给客户端.。
创建servlet
创建一个项目Maven
maven是一个IDEA的构建工具,可以帮助我们去构建测试,打包一个项目。

我这里是2022版本,或许和各位的界面不同,但是大同小异。
一个maven项目会有一个pom.xml的配置文件。

不同的maven有不同的坐标,一般不发布到maven的中央仓库其实不需关注。

目录结构

引入依赖
从maven中央仓库中下载安装。
中央仓库链接



复制完后放入pom.xml中就好了
不过要注意的是在pom.xml中增加一个dependencies的标签,然后将内容放在这个标签下就好。还有一点要注意,这个标签可以放入n个像servlet这样的依赖。

如果第一次使用maven,会显示红色。但是等依赖下载完毕就好了。
而下好的依赖的在一个影藏文件中,在电脑中的自己的用户目录里的.m文件中

下载好后就不需要自己

创建目录

这web.xml是给Tomcat的看的。相当于这个目录是给前端看的。
而里面的内容是固定的,粘贴我发的就好。
<!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>
复制的时候记住选择纯文本粘贴,不然小心粘贴一些特殊字符。
实现代码
public class hollewold extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}
doget:要完成的工作,就是根据请求,计算生成响应。

- http请求:tomcat收到请求,就会把这个请求按照http协议生成一个对象,而这个对象的的各个属性就是http的各个信息。
http响应:此处的响应对象是一个空的响应对象,需要我们根据业务逻辑设置响应信息,在doget中。比如body和header。只要把属性设置到这个resp对象中,tomcat就会自动响应对象,然后构造一个http响应字符串通过socket返回客户端。
@WebServlet("/hello")
public class hollewold extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//服务端在控制台中显示
System.out.println("hello world");
//在客户端中显示
resp.getWriter().write("hello world");//字符流
}
}
resp.getWriter().write(“hello world”);这句就会在html中的body中显示
- 创建一个类 HelloServlet , 继承自 HttpServlet
- 在这个类上方加上@WebServlet(“/hello”) 注解, 表示 Tomcat 收到的请求中, 路径为 /hello
的请求才会调用 HelloServlet 这个类的代码. - 重写 doGet 方法. doGet 的参数有两个,分别表示收到的 HTTP 请求 和要构造的 HTTP 响应. 这个方法会在 Tomcat 收到 GET 请求时触发
- HttpServletRequest 表示 HTTP 请求. Tomcat 按照 HTTP 请求的格式把 字符串 格式的请求转成了一个HttpServletRequest 对象. 后续想获取请求中的信息(方法, url, header, body 等)
- 都是通过这个对象来获取. HttpServletResponse 表示 HTTP 响应. 代码中把响应对象构造好(构造响应的状态码, header, body 等)
- resp.getWriter() 会获取到一个流对象, 通过这个流对象就可以写入一些数据,写入的数据会被构造成一个 HTTP 响应的 body 部分, Tomcat 会把整个响应转成字符串, 通过 socket 写回给浏览器。
规则:
- 创建的类需要继承自 HttpServlet
- 这个类需要使用 @WebServlet 注解关联上一个 HTTP 的路径
- 这个类需要实现doXXX 方法
打包
部署代码到tomcat,但是先需要打包。
在java中通常打包为jar,war。
而这里打包成war部署给tomcat的程序。war是tomcat的专属格式。

打包操作:
- 检查代码中是否存在一些依赖依赖,是否下载好
- 把代码进行编译,生成一堆.class文件。
- 这些点class文件以及web.xml,按照一定的格式进行大包。

打包成功

可以看见我们这里已经成功打包,但是要注意我们打包是.jar,不是.war,所以我们要对他命名。pom.xml中

- packaging字段表示打包的格式
- 下面的则是对打包进行命名
- 然后重新打包。


这里就成功了。打好的war包就是一个普通的压缩包,也可以用压缩软件查看,但是我们这里就将包给tomcat的webapps,然后他会在运行的时候自动解压缩。
验证程序

doGet表示必须要收到get请求,才能执行。

URL中hello_world是文件名,而hello则是@WebServlet(“/hello”)的hello。
URL:127.0.0.1:8080/文件名/@WebServlet(中的内容)
简化打包操作:下载smart tomcat的插件


使用Smart Tomcat插件:




这个呢和我们进行手动打包不一样,smart tomcat是会以项目名为url第一级目录,而手动以自己命名为url第一级目录。
Servlet的常见问题
404:表示你浏览器访问的资源,在服务器不存在。
1.你请求的路径写错了
2.路径写对了,但是war包没有被正确加载
405
1.发的请求的方法与代码不匹配
比如代码写的是doPost,你发的请求是个Get请求
2.虽然方法和代码匹配,但是忘了去掉super.doXXX.
500
意味着这你的服务器代码抛出了异常。
空白页面
意味着服务器没有返回数据。
Servlet API
1.HttpServlet
我们写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自 HttpServlet, 并重写其中的某些方法.
| 方法名称 | 调用时机 |
|---|---|
| init | 在 HttpServlet 实例化之后被调用一次 |
| destory | 在 HttpServlet 实例不再使用的时候调用一次 |
| service | 收到 HTTP 请求的时候调用 |
| doGet | 收到 GET 请求的时候调用(由 service 方法调用) |
| doPost | 收到 POST 请求的时候调用(由 service 方法调用) |
| doPut/doDelete/doOptions/… | 收到其他请求的时候调用(由 service 方法调用) |
init方法:HTTPServlet被实例化(首次收到匹配的请求时,会调用到)会调用一次,这个方法用来做些实例化相关的工作。
destory方法:这个方法是webapp被卸载前执行一次用来做一些收尾工作。(不靠谱,不建议,使用8005管理端口,来停止服务器,此时destroy)(8005端口配置一些业务外的配置)
service方法:每次收到路径匹配的请求,都会执行。(doGet/doPost其实是在service中被调用的,一般不会重写service,只是重写doXXX)
Servlet的生命周期:
初始:init
中年使用:service
结束:destory
网页出现乱码问题:
- 数据返回的时候,自身是一种编码方式(IDEA默认UTF-8)
- 浏览器在显示的时候,根据系统默认编码走(Windows 10 gbk)
- 编码对不上就会乱码。
java的char类型的是Unicode编码类型,而String是utf-8,而字符转为字符串就会自动转码。
resp.setContentType(“text/html,charse=utf-8”);
使用这个,就可将告诉浏览器,返回的数据是utf8格式
HttpServletRequest
一个HTTP请求,Tomcat收到请求后,就会解析生成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:唯一资源定位符
其实使用差别不大,URI可以当成URL使用。
getParameter
最常用的API之一
前端给后端传递数据,是非常常见的需求。
- 通过 query string 传递
query string 中的键值对,都是我们自己定义的;
比如我们定义了username,password;

@WebServlet("/getParameter")
public class getParameter extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username=req.getParameter("username");
String password=req.getParameter("password");
if (username==null){
System.out.println("没有username");
}
if (password==null){
System.out.println("没有password");
}
System.out.println(username+" "+password);
resp.getWriter().write("ok");
}
}
- 通过body传递(form)
相当于body里存的数据的格式就和query string一样,但是content-type是application/x-www-form-urlencoded,此时也是通过getParameter获取键值对。
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//给请求设置
req.setCharacterEncoding("utf8");
String username=req.getParameter("username");
String password=req.getParameter("password");
if (username==null){
System.out.println("没有username");
}
if (password==null){
System.out.println("没有password");
}
System.out.println(username+" "+password);
resp.getWriter().write("ok");
}
前端:
<form action="postParameter" method="POST">
<input type="text" name="username">
<input type="text" name="password">
<input type="submit" value="提交">
</form>
乱码了就用:
req.setCharacterEncoding(“utf8”);
将前端传来的数据进行编码转换。
- 通过body(json)
json格式也是键值对格式的数据,但是Servlet没有内置,我们还是要去中央库下载。


class User{
public String username;
public String password;
}
public class json extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
User user =objectMapper.readValue(req.getInputStream(),User.class);
System.out.println("username="+user.username+",password"+user.password);
resp.getWriter().write("ok");
}
}
readValue做的事
1.解析json字符串,转换成若干个键值对。
2.根据第2个参数user.class。反射的时候信息从点plus文件中获取。
3.遍历属性,根据属性的名字(通过反射),去上述准备好的键值对里查询。看看这个属性名字是否存在对应的value,如果存在,就把value赋一直到该属性中。
HttpServletResponse
| 方法 | 描述 |
|---|---|
| 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 中写入二进制格式数据. |
用header实现自动刷新
给HTTP响应 中,设置Refresh时间
@WebServlet("/refresh")
public class StatusServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Refresh","1");
resp.getWriter().write("time"+System.currentTimeMillis());
}
}
通过这个代码我们就可以每隔一秒就刷新一下数据
实现重定向
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(302);
resp.setHeader("Location","https:www.baidu.com");
//同等
resp.sendRedirect("https:www.baidu.com");
}
}
实现一个表白墙(服务端)(你们可以不用看,这个代码主要是我复习用的)
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.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 List<Message> messageList=new ArrayList<>();
@Override
protected 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);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理提交的新信息
Message message= objectMapper.readValue(req.getInputStream(),Message.class);
save(message);
System.out.println("消息提交成功!massage"+message);
//响应只是返回200报文,body为空,此时不需要额外处理,默认返回是200
}
// 读取数据
private List<Message> load() {
List<Message> messageList=new ArrayList<>();
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/BiaoBai?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("xietian1314");
try {
Connection connection=dataSource.getConnection();
String sql="select * from messge";
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("messge");
messageList.add(message);
}
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
return messageList;
}
//往数据库写数据
private void save(Message message) {
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/BiaoBai?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("xietian1314");
try {
Connection connection=dataSource.getConnection();
String sql="insert into message valuses(?,?,?)";
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) {
throw new RuntimeException(e);
}
}
}
链接了数据库。
小结:
很多网站,都是一套逻辑。
- 约定前后点的交互接口
- 实现服务器代码(通常可以用文件或者数据库存储数据)
- 实现客户端代码(通常会用ajax 构造请求,并使用一些js的webAPI操作页面内容)
session回话
Cookie保存用户身份标识,此时身份标识如何分配,以及省份信息具体如何存储(Session)会话,都是需要服务器的支持。
session:会给用户分配一个sessionID(令牌)同时记录下当前的一些身份信息(可以自定义),然后这个sessionID(令牌)就会被返回到浏览器的cookie中,然后浏览器访问服务器都会带着这个SessionID(令牌),然后服务器就可以识别出当前的用户身份。

| 方法 | 描述 |
|---|---|
| HttpSession getSession() | 在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null |
| Cookie[] getCookies() | 返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把Cookie 中的格式解析成键值对. |
(实现登录功能很重要)
getSession有一个参数boolean类型。
- 如果参数为false,getSession的行为是:
- 读取请求中cookie里的session ID。
- 在服务器这边根据session ID来查询对应的session对象。
- 如果查到了就会返回这个session对象,如果没有查到就返回null。
(实现登录功能很重要)
如果参数为true。getSession的行为是:
- 读取请求中cookie的sessionID。
- 在服务器这边根据session ID来查询对应的session对象。
- 如果查到了,就会返回这个session对象。
- 如果没有查到,就会创建一个session对象,同时生成一个session ID,把session ID为key,session 对象为value。把键值对存储到服务器里的一个哈希表中同时把session ID,同时set-cookie的方式返回给浏览器。
实现一个登录功能:
页面(前端,web):
<!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" value="username">
<input type="password" value="password">
<input type="submit" value="登录">
</form>
</body>
</html>
服务器(登录页)
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.先从请求中拿到用户名密码
String username=req.getParameter("username");
String password=req.getParameter("password");
req.setCharacterEncoding("utf8");
//验证密码
if (username==null|| password==null ||username.equals("")||password.equals("")) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前输入的用户名或者密码不为空");
return;
}
if (!username.equals("zhangsan")&&!username.equals("lisi")){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("用户名或密码有误");
return;
}
if (!password.equals("123456")){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("用户名或密码有误");
return;
}
//验证密码和用户名,创建一个Session会话。
//当前用户处于未登录的状态,此时请求的cookie中没有SessionID
HttpSession session= req.getSession(true);
//存储键值对,在之后服务器进行验证
session.setAttribute("username",username);
//登录成功后自动跳转
resp.sendRedirect("index");
}
}
服务器(响应页)
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
@Override
protected 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;
}
//上述都没有问题,接下来就直接生成一个动态页面
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("欢迎你"+username);
}
}
(部署到tomcat) Session ID也不会一直存在下去。比如服务器重启原来的session表中的内容就没有了,再次访问的时候就可能出现session ID无法查询到,于是就被识别成未登录状态了。
在IDEA中,为了方便调试程序,smart Tomcat,会在停止服务器的时候将会话持久保存,并且在下次启动的时候。自动会把会话恢复到内存中,此时绘画不会丢失。 Session ID也能查到。
Servlet上传文件
HttpServletRequest 类方法
| 方法 | 描述 |
|---|---|
| Part getPart(String name) | 获取请求中给定 name 的文件 |
| Collection getParts() | 获取所有的文件 |
Part 类方法
| 方法 | 描述 |
|---|---|
| String getSubmittedFileName() | 获取提交的文件名 |
| String getContentType() | 获取提交的文件类型 |
| long getSize() | 获取文件的大小 |
| void write(String path) | 把提交的文件数据写入磁盘文件 |

被折叠的 条评论
为什么被折叠?



