JavaEE
Servlet —— Tomcat, 初学 Servlet 程序
Servlet —— Smart Tomcat,以及一些访问出错可能的原因
目录
- Servlet API
- HttpServlet
- HttpServletRequest
- 获取 GET 请求中的参数
- 获取 POST 请求的参数
- HttpServletResponse
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 {@Overrideprotected 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 {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取 query string 中的键值对// 假设浏览器的请求形如 ?studentId=17&studentName=gujiuString 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 {@Overrideprotected 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 {@Overrideprotected 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 {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//约定, 浏览器 query string 传个参数//形如 type = 1//如果 type为 1, 返回200; 为 2, 返回 404; 为 3, 返回 500String 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 {@Overrideprotected 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 {@Overrideprotected 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");}
}
以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!