|
Servlet —— Tomcat, 初学 Servlet 程序
Servlet —— Smart Tomcat,以及一些访问出错可能的原因
Servlet API
虽然 Servlet
提供的类和方法很多, 但是我们最主要使用的就是三个:
- HttpServlet
- HttpServletRequest
- HttpServletResponse
HttpServlet
核心方法
方法名称 | 调用时机 |
---|---|
init | 在 HttpServlet 实例化之后被调用一次 |
destory | 在 HttpServlet 实例不再使用的时候调用一次 (不一定真的能调用到) |
service | 收到 HTTP 请求的时候调用 |
doGet | 收到 GET 请求的时候调用(由 service 方法调用) |
doPost | 收到 POST 请求的时候调用(由 service 方法调用) |
doPut/doDelete/doOptions/… | 收到其他请求的时候调用(由 service 方法调用) |
#
init
创建出 HttpServlet 实例会调用一次. init
方法的作用是用来初始化.
#
destroy 不一定真的能调用到的原因:
当 Tomcat
关闭, 则不再调用 HttpServlet, Tomcat 关闭有两种方法
- 杀进程, 比如: 点击 idea 红色方框, 或者用任务管理器结束任务. 此时
destroy
无法被调用. - 通过
8005
端口, 给 Tomcat 发送一个关闭操作, 这时 Tomcat 就可以正常关闭, 就能调用到destroy
.
#
service
Tomcat 收到请求, 实际上是先调用 service
, 在 service 里面再根据方法, 调用相应的 doXXX
.
常见面试题: 谈谈 Servlet 的生命周期
Servlet 生命周期描述的是 Servlet
创建到销毁的过程:
- 当一个请求从
HTTP
服务器转发给 Servlet 容器时,容器检查对应的 Servlet 是否创建,没有创建就实例化该Servlet
,并调用 init() 方法,init()方法只调用一次,之后的请求都从第二步开始执行; - 请求进入 service() 方法,根据请求类型转发给对应的方法处理,如doGet, doPost, 等等
- 容器停止前,调用 destory() 方法,进行清理操作,该方法只调用一次,随后
JVM
回收资源。
HttpServletRequest
核心方法
方法 | 描述 |
---|---|
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 对象. |
代码示例
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 ShowRequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//声明响应 body 是 html 结构的数据
resp.setContentType("text/html");
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(req.getProtocol());
stringBuilder.append("<br>");//换行
stringBuilder.append(req.getMethod());
stringBuilder.append("<br>");
stringBuilder.append(req.getRequestURI());
stringBuilder.append("<br>");
stringBuilder.append(req.getContextPath());
stringBuilder.append("<br>");
stringBuilder.append(req.getQueryString());
stringBuilder.append("<br>");
//把请求的 header 也拼进来
Enumeration<String> headerName = req.getHeaderNames();
while(headerName.hasMoreElements()) {
String name = headerName.nextElement();
String value = req.getHeader(name);
stringBuilder.append(name + ": " + value);
stringBuilder.append("<br>");
}
resp.getWriter().write(stringBuilder.toString());
}
}
运行结果展示:
获取 GET 请求中的参数
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 GetParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 query string 中的键值对
// 假设浏览器的请求形如 ?studentId=17&studentName=gujiu
String studentId = req.getParameter("studentId");
String studentName = req.getParameter("studentName");
System.out.println(studentId);
System.out.println(studentName);
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write(studentId + ", " + studentName);
}
}
运行结果展示:
getParameter
获取键值对的时候:
- 如果键不存在, 得到的就是 null
- 如果键存在, 值不存在, 得到的就是 “”
# 注意事项 #
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write(studentId + ", " + studentName);
这两行代码顺序不能颠倒, 务必要先设置所有的 header 最后再设置 body.
获取 POST 请求的参数
请求的 body 是 x-www-from-urlencoded
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 GetParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 通过 body 获取, 发 post 请求
// 请求这里设置的 utf8 , 是告诉 servlet (tomcat) 如何解析
req.setCharacterEncoding("utf8");
String studentId = req.getParameter("studentId");
String studentName = req.getParameter("studentName");
System.out.println(studentId);
System.out.println(studentName);
// 响应这里的 utf8 是告诉浏览器如何解析
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(studentId + ", " + studentName);
}
}
使用 postman 发送 POST
请求, 结果展示:
抓包展示:
或者可以用 from 表单的形式发送 POST 请求, 可参考HTTP —— HTTP 响应详解, 构造 HTTP 请求
请求的 body 是 json
如何解析 json 格式呢?
Servlet 没有内置 json 解析, 自己写解析, 比较麻烦 (json 支持嵌套). 我们更好的选择, 是使用现成的第三方库 (市面上又很多的 json 第三方库, 比如: fastson, jackjson, gson…).
我们使用 jackson
.
#
我们在中央仓库 搜索 jackson, 如图选择 Jackson Databind
#
选择一个版本 拷贝到 pom.xml 里面的 <dependencies>
标签中. 形如:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4.1</version>
</dependency>
如果标红记得刷新.
#
写代码
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 Student {
// 这个类中的属性务必是 public 或者带有 public 的 getter/ setter (否则 jackson 无法访问这个对象的属性)
public int studentId;
public String studentName;
//这个类务必要有无参版本的构造方法
}
@WebServlet("/json")
public class JsonServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 此处假设请求的 body 格式为
// { studentId: 17, studentName: gujiu }
// ObjectMapper 是 jackson 提供的核心的类
// 其中一个方法叫做 readValue, 把 json 格式的数据转成 java 的对象
// 还有一个方法叫做 writeValueAsString, 把 java 对象转成 json 格式的字符串
ObjectMapper objectMapper = new ObjectMapper();
// readValue 第一个参数可以是字符串, 也可以是输入流
// 第二个参数, 是一个类对象, 也就是要解析出来的结果的对象的类;
Student student = objectMapper.readValue(req.getInputStream(), Student.class);
System.out.println(student.studentId);
System.out.println(student.studentName);
resp.setContentType("application/json; charset=utf8");
// 两种写法均可 //resp.getWriter().write(objectMapper.writeValueAsString(student));
objectMapper.writeValue(resp.getWriter(), student);
}
}
#
使用 postman 构造一个 POST 请求
#
运行结果展示
HttpServletResponse
doXXX 这样的方法里的 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 中写入二进制格式数据. |
代码示例: 设置状态码
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 {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//约定, 浏览器 query string 传个参数
//形如 type = 1
//如果 type为 1, 返回200; 为 2, 返回 404; 为 3, 返回 500
String type = req.getParameter("type");
if(type.equals("1")) {
resp.setStatus(200);
} else if(type.equals("2")) {
resp.setStatus(404);
//返回一个 tomcat 自带的 404 效果
//resp.sendError(404);
} else if(type.equals("3")) {
resp.setStatus(500);
} else {
resp.setStatus(504);
}
}
}
运行结果展示:
代码示例: 设置响应的 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;
/**
* Description:设置响应的 header 实现页面自动刷新
*/
@WebServlet("/autoRefersh")
public class AutoRefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//直接返回响应就好
resp.setHeader("refresh", "2");
resp.getWriter().write(System.currentTimeMillis() + "");
}
}
# 注意 #
我们设置 2s 刷新一次, 但并不是精确的 2000ms, 会比 2000ms 略多一点, 调度要消耗时间/ 网络传输消耗时间/ 服务器响应消耗时间, 再加上对于 ms 级别的计时存在误差.
代码示例: 构造重定向
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("/redirct")
public class RedirctServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//进行重定向, 收到请求, 跳转到到 我的主页
resp.setStatus(302);
resp.setHeader("Location", "https://blog.csdn.net/m0_58592142?spm=1000.2115.3001.5343");
}
}
|
以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!