目录
什么是 servlet?
Servlet 是做甚的?
如何编写一个 Servlet 程序?
解析访问出错情况
Servlet 的运行原理
1. 接收请求
2. 根据请求计算响应
3. 返回响应
Servlet API 详解
HTTPServlet
HttpServletRequset
HttpServletResponse
Servlet 是一种实现动态页面的技术, 是一组 Tomcat(一个HTTP服务器) 提供给程序员的一种 API , 帮助程序员简单高效的开发一个 web app
Servlet 也是是一些遵从Java Servlet API的Java类,这些Java类可以响应请求。尽管Servlet可以响应任意类型的请求,但是它们使用最广泛的是响应web方面的请求。 Servlet必须部署在Java servlet容器才能使用。
总的来说, servlet是完成 java Web 中处理请求和发送响应过程的一种程序,是为了解决实现动态页面的技术而衍生的东西
允许程序员注册一个类, 在 Tomcat 收到的某个特定的请求的时候, 执行这个类中的一些代码
帮助程序员解析 HTTP 请求, 把 HTTP 请求从一个字符串解析成一个 HttpRequest 对象
帮助程序员构造 HTTP 响应, 程序员只要给指定的 HttpResponse 对象填写一些属性字段, Servlet 就会自动的按照 HTTP 协议的方式构造出一个 HTTP 响应字符串, 并通过 Socket 写回给客户端
创建项目
先创建一个 Maven 项目
创建完毕弹出 一个对话框 选择 Enable Auto-Import
引入依赖
我们需要在 pom.xml 中引入 Servlet 依赖的 jar 包 (3.1.0)
javax.servlet
javax.servlet-api
3.1.0
provided
Servlet 的版本要和 Tomcat 的版本匹配
Groupld : 组织名称
Artifactld : 项目名称
Version : 表示版
3. 创建目录
目录介绍
Src : 源代码所在的目录
main/java :源代码的根目录, 后续创建 .Java 文件就放到这个目录中
main/resources : 表示项目的一些资源文件所在的目录
test/java : 表示测试代码的根目录
创建 webapp 目录 (在 main 目录下) : 存放以下静态文件 HTML , CSS
创建 WEB-INF 目录(webapp下) 并创建一个 web.xml 写入以下代码
Archetype Created Web Application
4. 编写代码
// 2.
@WebServlet("/hello")
//1.
public class HelloServlet extends HttpServlet {
@Override
//doGet就是 根据请求计算响应 req 请求 resp响应
// 3.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp); //直接返回错误
//这是在服务器的控制台中, 打印了字符串.(服务器看到了, 客户端看不到)
System.out.println("hello world!");
// 这是在 resp 的 body 中写入 hello world 字符串,这是内容就会被 HTTP 响应返回给浏览器, 显示到浏览器页面上
resp.getWriter().write("hello world!!");
}
}
此时代码就可以进行运行了, 但是现在不是使用 main 作为程序的入口, mian 方法现在会包含在 Tomcat 中, 这个代码 Tomcat 会在合适的时期进行调用
这个代码只是 Tomcat 中的一小部分逻辑
要想被调用, 就得满足以下这三个条件
创建类的时候需要继承自 HttpServlet
需要使用 @WebServlet 注解, 加上一个 HTTP路径 ("/路径名")
实现 doXXX 方法
5. 打包程序
要打成 war 类型的包, war 类型的包才能被 Tomcat 识别到
要想打包为 war 包,就必须进行配置, 一般情况下默认是 jar 包
war
//描述包名
hello_servlet
war 包和 jar 包的区别
jar 包是普通的 java 程序打包的结果. 里面会包含一些 .class 文件. war 包是 java web 的程序, 里面除了会包含 .class 文件之外, 还会包含 HTML, CSS, JavaScript, 图 片, 以及其他的 jar 包. 打成 war 包格式才能被 Tomcat 识别
4.0.0
org.example
Servlet
1.0-SNAPSHOT
8
8
javax.servlet
javax.servlet-api
3.1.0
provided
com.fasterxml.jackson.core
jackson-databind
2.15.1
war
hello_servlet
6. 部署程序
把 war 包拷贝到 Tomcat 的 webapps 目录下 ,启动
然后启动 Tomcat 就会自动解压缩 war 包
7. 验证程序
在浏览器中输入以下链接就可以进行打开运行 servlet 程序了
127.0.0.1:8080/hello_servlet(context 路径)/hello()
使用 IDEA 插件 smartTomcat 进行 部署
安装 Tomcat 插件
配置 smartTomcat
使用 Tomcat 插件省去了 打包 和 部署 两个步骤, 可以一键运行servlet程序
404
表示用户访问的资源不存在, 大概就是 URL 的路径写的不正确
例如: 少些了 Context Path 或者是 Servlet Path
405
表示对应的 HTTP 请求方法没有实现
例如: 没有实现 doGet 方法
500
Servlet 代码中抛出异常导致的
空页面
没有 resp.getWritter().write() 操作
响应 body 中的数据就是空数据
无法访问此网站
一般情况下就是 Tomcat 启动失败了
注解 @WebServlet("/xxx") 中的 / 一定得注意写上
作为一个程序员不光要写出好的代码, 还得要很高效的调试代码
Servlet 中没有 main 方法他是怎么运行的?
- Tomcat 通过 Socket 读取到这个请求(一个字符串). 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个 webapp, 再通过 webapp ,再通过 Servlet Path 确定一个具体的类, 再根据当亲请求的方法(GET/POST/...), 决定调用这个类的 doGet 或者 doPost 等方法, 此时我们的代码中的 doGet / doPsot 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息
下面是详细的 servlet 的基本运行原理
写 Servlet 代码的时候, 第一步就是 创建类, 继承自 HttpServlet , 并重写其中的某些方法
核心方法
方法名称 |
调用时机 |
init |
在 HTTPServlet 实例化之后被调用一次 |
destory |
在 HttpServlet 实例不再使用的时候调用一次 |
service |
收到 HTTP 请求的时候调用 |
doGet |
收到 GET 请求的时候调用(由 service 方法调用) |
doPost |
收到 Post 请求的时候调用(由 service 方法调用) |
doPut/doDelete/doOptions |
收到其他请求的时候调用(由 service 方法调用) |
代码示例: 处理 GET 请求
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
//doGet就是 根据请求计算响应 req 请求 resp响应
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp); //直接返回错误
//这是在服务器的控制台中, 打印了字符串.(服务器看到了, 客户端看不到)
System.out.println("hello world!");
// 这是在 resp 的 body 中写入 hello world 字符串,这是内容就会被 HTTP 响应返回给浏览器, 显示到浏览器页面上
resp.getWriter().write("hello world!!");
}
}
代码示例: 处理 POST 请求
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
//doGet就是 根据请求计算响应 req 请求 resp响应
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp); //直接返回错误
//这是在服务器的控制台中, 打印了字符串.(服务器看到了, 客户端看不到)
System.out.println("dopost");
}
}
使用 PostMan 发送请求对应的结果
当 Tomcat 通过 Socket API 读取到 HTTP 请求(字符串), 并且按照 HTTP 协议的格式把字符解析为 HttpServletRequest 对象 核心方法
方法 |
描述 |
String getprotocol() |
返回请求协议的名称和版本 |
String getMethod() |
返回请求的 HTTP 方法的名称, 例如, GET. POST 或者 PUT |
String getRequestURL() |
返回该请求的一部分 URL |
String getContextPath() |
返回 ContentPath |
String getQueryString() |
返回包含在路径后的请求 URL 中的查询字符串 |
Enumeration getParaeterNames() |
返回一个 String 对象的枚举, 包含在该请求中包含的参数的名称 |
String getParameter(String name) |
以字符串形式返回请求参数的值, 或者如果参数不存在则返回 null |
Enumeration getHeaderNames() |
返回一个枚举, 包含在该请求中包含的所有头名 |
String getHeader(String name) |
以字符串形式返回指定的请求头的值 |
String getChatacterEncoding() |
返回请求主体中使用的字符编码的名称 |
String getContentTyoe() |
返回请求主体的 MIME 类型, 如果不知道则返回 null |
Int getContentLength() |
以字节为单位返回请求主体的长度, 并提供输入流, 或者如果长度未知则返回 -1 |
InputStream getInputStream() |
用于读取请求的 body 内容,返回一个 InputStream 对象 |
上面的方法可以 获取 到一个请求中的各个方面的信息
注意: 请求对象是服务器收到的内容, 不应该修改, 因此上面的方法也都只是 "读" 方法, 而不是 "写" 方法
代码示例:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
@WebServlet("/showRequest")
public class ShowRequest extends HelloServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
StringBuilder result = new StringBuilder();
//获取请求的协议的名称和版本
result.append(req.getProtocol());
result.append("
");
//返回请求的HTTP方法的名称
result.append(req.getMethod());
result.append("
");
//获取 URL 的一部分, 唯一资源标识符
result.append(req.getRequestURI());
result.append("
");
//获取 URL
result.append(req.getRequestURL());
result.append("
");
//获取 contextPath
result.append(req.getContextPath());
result.append("
");
//获取 servletPath
result.append(req.getServletPath());
result.append("
");
//获取包含在路径后的请求 URL 中的查询字符串
result.append(req.getQueryString());
result.append("
");
//返回一个枚举, 包含在该请求中包含的所有头名
result.append(req.getHeaderNames());
result.append("
");
//获取一 String 对象的枚举, 包含在该请求中包含的参数的名称
//result.append(req.getParameterValues(""));
//以字符串的形式返回指定的请求头的值
//result.append(req.getHeader());
result.append("==========================
");
//创建枚举进行接收, 所有的header
Enumeration headerNames = req.getHeaderNames();
//循环读取枚举中的数据
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValues = req.getHeader(headerName);
result.append(headerName + ": " + headerValues + "
");
}
//在响应中设置 body 的类型, 方便浏览器进行解析
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write(result.toString());
}
}
代码示例 : 获取 GET 请求中的参数
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/getParameter")
public class GetParameter extends HelloServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//前端通过 url 的 query string 传递 username 和 password 两个属性
String username = req.getParameter("username");
if(username == null) {
System.out.println("username 这个属性在 query string 中不存在");
}
String password = req.getParameter("password");
if(password == null) {
System.out.println("password 这个属性在 query string 中不存在");
}
System.out.println("username = " + username + " password = " + password );
resp.getWriter().write("ok");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//给请求设置字符编码 //告诉服务器字符编码
req.setCharacterEncoding("utf8");
//前端通过 body 以 form 表单的形式把 username 和 password 传给服务区
String username = req.getParameter("username");
String password = req.getParameter("password");
if(username == null) {
System.out.println("username 这个 key 在 body 中不存在");
}
if(password == null) {
System.out.println("password 这个 key 在 body 中不存在");
}
System.out.println("username= "+ username + " password= " + password);
resp.getWriter().write("ok");
}
}
当没有使用 query String 方法传递值的时候, getParameter 的时候,获取的值为 null
使用之后, 此时说明服务器已经获取到客户端传递过来的参数
Servlet 中的 doXX 放法的目的就是根据请求计算得到响应, 然后把响应的数据设置到 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 中写入二进制格式数据. |
展示常用方法的代码及运行截图
代码示例: 设置状态码
@WebServlet("/status")
public class StatusServlet extends HelloServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(200);
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("返回 200 响应!");
}
}
代码示例 : 自动刷新
@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//每隔 1s 刷新一次
resp.setHeader("Refresh","1");
//获取毫秒级别的时间戳
resp.getWriter().write("time = " + System.currentTimeMillis());
}
}
每隔一秒进行刷新, 这里是 过了很多秒截的图
对时间进行格式化输出
@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//每隔 1s 刷新一次
resp.setHeader("Refresh","1");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
resp.getWriter().write("time = " + format.format(System.currentTimeMillis()));
}
}
总结:
Servlet是一个用于扩展服务器功能的Java编程语言类。它是一个服务器端组件,通过HTTP或HTTPS协议接收并响应来自客户端(通常是Web浏览器)的请求。
Servlet通常用于构建动态Web应用程序。它提供了一种处理客户端请求、执行业务逻辑和生成动态内容(如HTML页面、XML、JSON或其他类型的数据)的方式。Servlet是Java Enterprise Edition(Java EE)平台的一部分,通常部署在Web服务器上。