Servlet 是 Java 编写的服务端程序,用于处理客户端(通常是浏览器)发送的请求,并向客户端返回响应。它是Java EE 规范的一部分,提供了一种在服务器上动态生成内容的方式。
Servlet 是基于Java 的实现的,因此具有跨平台性,可以在不同的操作系统上运行。它遵循 Java 的安全性和可靠性,也能充分利用 Java 语言的优势,如面向对象编程和丰富的类库。
Servlet 主要用于开发 Web应用程序,如网站、Web服务、Web应用程序后端等。Tomcat 容器会在接收到请求的时候将其传递给相应的Servlet 类进行处理。同时,Servlet 可以根据请求参数、会话状态、数据库查询等动态生成HTML、XML、JSON等内容,然后将响应发送给客户端。
总而言之,Servlet 是一种实现动态页面的技术,是一组 Tomcat 提供给开发人员的 API,帮助开发人员能够简单高效的开发 Web 程序。
Servlet 在 Java Web 开发中主要负责以下工作:
注册 Servlet 类并处理 HTTP 请求:
web.xml
文件或使用注解来配置 Servlet 映射,将 Servlet 与特定的URL
或URL
关联起来。解析 HTTP 请求:
HttpRequest
对象。HttpRequest
对象提供了各种方法,使得开发者能够轻松地获取 HTTP 请求的方法(GET、POST等)、请求头、请求参数、会话信息等。构造 HTTP 响应:
HttpResponse
对象的属性字段,Servlet 会自动根据 HTTP 协议的方式构造出一个 HTTP 响应字符串,并通过 Socket 写回给客户端。HttpResponse
对象提供了设置响应状态码、响应头、响应内容等的方法,开发者可以根据业务需求进行设置,Servlet 会将设置的响应信息返回给客户端。总的来说,Servlet 作为 Java 的服务器端技术,帮助开发者处理 HTTP 请求和响应,允许注册和执行特定的 Java 类,解析 HTTP 请求,并根据设置的 HttpResponse
对象构造 HTTP 响应。这使得 Java 开发者能够轻松地开发动态、交互式的 Web 应用程序。
Servlet 和 Tomcat 之间有着密切的关系,可以说它们是一对密不可分的组合,以下是对它们之间关系的解释:
- Tomcat 是 Servlet 的容器,它提供了 Servlet 的执行环境和支持,使得 Servlet 能够在 Tomcat 服务器上运行。
- 当在 Tomcat 上部署一个 Web 应用程序时,Tomcat 会负责加载和初始化其中的 Servlet 对象,并在接收到 HTTP 请求时调用相应的Servlet 对象来处理请求和生成响应。
- Tomcat 还提供了管理和监控 Web 应用程序的功能,可以通过 Tomcat 的管理界面来管理已部署的 Servlet 和 Web 应用程序。
总结而言,Servlet 是 Java 编写的服务器端程序,而 Tomcat 是一个能够执行和管理 Servlet 的容器。它们共同组成了Java Web 应用程序的基础架构,使得开发者能够构建动态、交互式的 Web 应用程序。
Maven是什么:
Maven 项目是基于 Apache Maven 构建工具的 Java 项目。Maven 是一个流行的项目管理和构建工具,旨在简化和标准化 Java 项目的构建过程。通过 Maven,开发者可以更轻松地管理项目依赖、构建项目、运行单元测试、生成项目文档等。
一个典型的 Maven 项目通常包含以下几个重要部分:
pom,xml
文件:pom.xml
是 Maven 项目的核心配置文件,它位于项目的根目录下。pom.xml
文件用于描述项目的元数据信息(如项目的名称、版本、描述等),定义项目的依赖关系(所需的第三方库和插件),配置项目的构建过程(编译、打包等),以及其他项目相关配置。
src
目录:该目录是项目的源代码和资源文件的根目录。通常包含两个子目录:
src/main
:包含主要的源代码和资源文件;src/test
:包含单元测试代码和资源。target
目录:该目录是 Maven 构建生成的输出目录,其中包含构建后的项目生成的输出文件,如编译后的类文件、打包后的JAR
文件和WAR
文件。
Maven 生命周期和插件:Maven 定义了一套标准的生命周期和插件,用于在构建过程中执行不同的任务。开发者可以通过配置pom.xml
文件来定义项目在不同构建阶段执行的任务,如编译、测试、打包、部署等。
使用 IDEA 创建 Maven 项目:
Build System
选项中选择 Maven,注意项目路径不要出现中文或其他特殊符号。当 Maven 项目创建完成后,会自动生成一个 pom.xml
文件,我们需要在 pom.xml
文件中引入 Servlet API 所依赖的 JAR
包。
servlet
,一般第一个结果就是我们所需要的 API。关于 Servlet 和 Tomcat 版本的匹配问题:
Maven xml
复制粘贴到pom.xml
文件中
注意要新增一个 dependencies
标签,将其复制到此标签中:
当创建完 Maven 项目后,IDEA 会自动帮我们生成如下的目录结构:
这些目录中:
src
表示源代码所在的目录;main/java
表示源代码的根目录,后续创建 .java
文件就放到这个目录中;main/resources
表示项目的一些资源文件所在的目录;test/java
表示测试代码的根目录。有了这些目录还不够,我们还需要创建以下新的目录和文件。
1. 创建 webapp
目录
在 main
目录下,创建一个与 java
目录并列的 webapp
目录,该目录用于存放 Web 应用程序的 Web 资源,例如 HTML 页面、JSP文件、CSS样式、JavaScript脚本等。
2. 创建 WEB-INF
目录
在 webapp
目录下,创建一个名为 WEB-INF
的目录。该目录是 Web 应用程序中的特殊目录,用于存放 Web 应用程序的配置和受保护的资源。
3. 创建 web.xml
文件
在 WEB-INF
目录下,创建一个名为 web.xml
的文件,该文件是 Java Web 应用程序的部署描述符,用于配置 Servlet、Filter、Listener 等 Web 组件以及其他应用程序相关的配置信息。
4. 编写 web.xml
文件
往 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 Applicationdisplay-name>
web-app>
在 java
目录中创建一个 HelloServlet
类,代码如下:
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
System.out.println("hello world");
resp.getWriter().write("hello world");
}
}
对这段代码的解释:
HttpServlet
的 HelloServlet
类;@WebServlet("/hello")
的注解,用于指定 Servlet 的 URL
映射。在这个示例中,Servlet 将被映射到 URL
路径为/hello
,当客户端发起对/hello
的 HTTP 请求时,HelloServlet
将被调用。doGet
方法,doGet
的参数有两个,分别表示收到的 HTTP 请求和要构造的 HTTP 响应。这个方法会在 Tomcat 收到 GET
请求时触发被 Tomcat 自动调用。HttpServletRequest
表示 HTTP 请求,Tomcat 按照 HTTP 请求的格式把字符串格式的请求转成了一个 HttpServletRequest
对象,后续想获取请求中的信息(比如方法、url
、header
、body
等)都是通过这个对象来获取。HttpServletResponse
表示 HTTP 响应,需要程序员在代码中把这个对象构造好(如构造响应的状态码、header
、body
等)。resp.getWriter()
会获取到一个 Writer
字符流对象,通过这个流对象就可以写入一些数据,写入的数据会被构造成一个 HTTP 响应的 body
部分,Tomcat 会把整个响应转成字符串,通过 socket
写回给客户端。虽然这几行代码很少,但是缺包含了大量的信息:
- 在 Servlet 程序的代码中,不需要自己编写
main
方法作为程序的入口了,因为main
方法已经包含在 Tomcat 里面了,我们自己编写的代码会在合适的时候被 Tomcat 所调用。- 我们写的代码并不是一个完整的程序,而是 Tomcat 这个程序的一小部分逻辑,如果要想在合适的时候正确的被 Tomcat 调用,需要满足一下三个条件:
1)创建的类需要继承自HttpServlet
类;
2)这个类需要使用@WebServlet
注解关联上一个 HTTP 的路径;
3) 这个类需要实现doXXX
方法。
View -> View -> Tool Window -> Maven
打开。Lifecycle
,双击 package
即可进行打包打包成功后,可以看到在 target
目录下,生成了一个 JAR
包:
但是,这样的 JAR
包并不是我们所需要的,Tomcat 需要识别的是另一种 WAR
包的形式。并且这个包的名字也太复杂了,我们希望有一个简单的名字。
pom.xml
配置文件,以生成 WAR
包和修改 WAR
包名称pom.xml
中新增一个 packing
标签,表示打包的方式是打一个 WAR
包;build
标签,内置一个 finalName
标签,表示打出的 WAR
包的名字是 hello_servlet
。将上面打包形成的 WAR
包文件复制到 Tomcat 的 webapps
目录下:
运行 Tomcat,会将其自动解压,此时便部署完成。
此时通过浏览器访问 http://127.0.0.1:8080/hello_servlet/hello
,就可以看到以下结果了:
此时,我们的第一个 Servlet 程序就已经完成了,如果看到这里,你也会相信这就是学习到目前为止的最麻烦的一个hello world
程序了吧。
以上的手动打包,然后拷贝到 Tomcat 的过程显得非常繁琐,但是 IDEA 上为我们提供了更加方便的打包部署程序的插件。“Smart Tomcat” 插件就是是为了简化和改进在 IntelliJ IDEA 中使用 Apache Tomcat 的体验而开发的。使用 “Smart Tomcat” 插件,可以更方便地配置和管理 Tomcat 服务器,轻松部署和调试 Java Web 应用程序。
在 IDEA 上打开:File -> Settings -> plugins
,然后搜索 Smart Tomcat,然后按照重启 IDEA 即可。
此时 Tomcat 日志就会输出在 IDEA 的控制台中,并且不会出现乱码。
404 表示用户访问的资源不存在,大概率是 URL
的路径写的不正确。
错误示例一:少写了 “Context Path”,直接通过 “/hello” 访问服务器
错误示例二:少写了 “Servlet Path”,直接通过 “/hello_servlet” 访问服务器
405 表示对应的 HTTP 请求方法没有实现。
例如,现在将
HelloServlet
类中重写的doGet
方法注释掉,然后重启 Tomcat 再次访问服务器
出现 500 的原因往往是 Servlet 代码中抛出异常导致的。
例如,修改
HelloServlet
中的doGet
方法如下,然后重启 Tomcat 并访问服务器。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String s = null;
resp.getWriter().write(s);
}
此时出现了空指针异常,表明服务器内部出错,因此返回的响应码为 500。
出现这种情况一般就是没有启动 Tomcat 或者启动 Tomcat 服务器失败。
比如,当 “Servlet Path” 写错的时候,就会出现启动 Tomcat 服务器失败。
此时,启动 Tomcat,就会出错:
Servlet 是 Java 中用于处理 Web 请求和响应的组件。它运行在 Web 服务器内部,通过 Web 容器(如Apache Tomcat)来管理其生命周期和请求调度。
Servlet运行的主要原理如下:
Web容器初始化:当 Web 服务器启动时,Web 容器会加载并初始化 Servlet 。在初始化过程中,Web 容器会创建 Servlet 实例,并调用其 init()
方法。在这个方法中,Servlet 可以进行一些初始化操作,例如加载配置信息、建立数据库连接等。
接收HTTP请求:当用户在浏览器中访问一个URL
时,Web服务器接收到
HTTP`请求。
请求分发:Web 容器根据请求的 URL
和部署描述符(web.xml
)中的配置信息来确定应该由哪个 Servlet 来处理该请求。
调用 Servlet 的 service()
方法:一旦确定了要由哪个 Servlet 处理请求,Web 容器会在 Servlet 实例上调用 service()
方法,并将请求和响应对象作为参数传递给它。
Servlet 处理请求:在 service()
方法中,Servlet 根据 HTTP 请求类型(GET、POST、PUT等)来执行相应的逻辑,处理请求并生成响应。
返回 HTTP 响应:处理完请求后,Servlet 会将生成的 HTTP 响应写回到响应对象中。
销毁:在 Web 服务器关闭时或 Servlet 不再需要时,Web 容器会调用 Servlet 的 destroy()
方法,使其有机会进行一些资源释放和清理工作。
Servlet 的生命周期主要包括以下三个阶段:
初始化(Initialization):
web.xml
或注解配置)并加载 Servlet 类。init(ServletConfig config)
方法来进行初始化。init()
方法只会在 Servlet 第一次被加载时调用一次,用于执行一些初始化操作,比如加载配置信息、建立数据库连接等。请求处理(Request Handling):
service(ServletRequest request, ServletResponse response)
方法。service()
方法根据请求类型(GET、POST、PUT 等)来调用相应的 doXXX()
方法(例如 doGet()
、doPost()
、doPut()
等)来处理请求。销毁(Destruction):
destroy()
方法来进行一些资源释放和清理操作。destroy()
方法只会在 Servlet 卸载之前调用一次,用于执行一些善后工作,比如关闭数据库连接、释放资源等。整个 Servlet 生命周期如下图所示:
注意:
init()
和 destroy()
方法只会调用一次。service()
方法,并且多个请求可以并发地访问同一个 Servlet 实例,因此开发者应确保 Servlet 的线程安全性。Servlet API 是用于开发 Java Servlet 程序的一组接口和类的集合。它提供了一种标准化的方式来处理 Web 请求和响应,使开发人员能够编写基于 Java 的 Web 应用程序。
Servlet API 位于 Java EE 规范中,开发人员可以通过 Java Servlet 规范来了解这些接口和类的详细信息。在 Java 中,Servlet API 通常由 javax.servlet
包和 javax.servlet.http
包中的类和接口组成。
下面是 Servlet API 中一些核心的类和接口以及它们的简要说明:
类/接口 | 说明 |
---|---|
javax.servlet.Servlet |
Servlet接口,所有Servlet类都必须实现该接口。 |
javax.servlet.GenericServlet |
Servlet抽象类,实现了Servlet接口的大部分方法。 |
javax.servlet.http.HttpServlet |
HttpServlet类,用于处理HTTP相关的请求和响应。 |
javax.servlet.ServletConfig |
接口,用于获取Servlet的初始化参数。 |
javax.servlet.ServletContext |
接口,用于在整个Web应用程序中共享信息。 |
javax.servlet.ServletException |
异常类,Servlet可能抛出的异常的基类。 |
javax.servlet.ServletRequest |
接口,用于表示客户端请求的对象。 |
javax.servlet.ServletResponse |
接口,用于表示Servlet的响应对象。 |
javax.servlet.http.HttpServletRequest |
表示HTTP请求的接口,扩展自ServletRequest。 |
javax.servlet.http.HttpServletResponse |
表示HTTP响应的接口,扩展自ServletResponse。 |
javax.servlet.http.HttpSession |
用于在客户端和服务器之间维护状态的接口。 |
javax.servlet.RequestDispatcher |
接口,用于将请求转发到其他资源(Servlet、JSP等)。 |
javax.servlet.Filter |
过滤器接口,用于在请求和响应之间执行过滤操作。 |
javax.servlet.FilterChain |
过滤器链,用于在多个过滤器之间传递请求和响应。 |
HttpServlet
是 Java Servlet API 中的一个抽象类,它扩展了 GenericServlet
类,并为处理基于 HTTP 协议的请求和响应提供了更方便的方法。HttpServlet
是编写基于 HTTP 的 Servlet 的常用基类。
以下是 HttpServlet
类中常用的方法:
方法 | 说明 |
---|---|
void init(ServletConfig config) |
在 Servlet 实例被创建后立即调用,用于初始化该 Servlet。 |
void destroy() |
在 Servlet 容器正常关闭时调用,用于释放资源和清理操作。 |
void doGet(HttpServletRequest request, HttpServletResponse response) |
处理 HTTP GET 请求。 |
void doPost(HttpServletRequest request, HttpServletResponse response) |
处理 HTTP POST 请求。 |
void doPut(HttpServletRequest request, HttpServletResponse response) |
处理 HTTP PUT 请求。 |
void doDelete(HttpServletRequest request, HttpServletResponse response) |
处理 HTTP DELETE 请求。 |
void service(HttpServletRequest request, HttpServletResponse response) |
根据请求的 HTTP 方法调用相应的 doXXX 方法进行处理。 |
ServletConfig getServletConfig() |
获取当前 Servlet 的配置信息。 |
String getServletInfo() |
获取 Servlet 的描述信息。 |
String getInitParameter(String name) |
获取指定名称的初始化参数值。 |
Enumeration |
获取所有初始化参数名称的枚举。 |
ServletContext getServletContext() |
获取 Servlet 上下文对象,用于访问 Servlet 环境的信息。 |
String getServletName() |
获取 Servlet 的名称。 |
void log(String msg) |
记录日志消息到 Servlet 容器的日志系统。 |
void log(String msg, Throwable throwable) |
记录异常信息到 Servlet 容器的日志系统。 |
void setInitParameter(String name, String value) |
设置初始化参数的值。 |
boolean isAsyncSupported() |
检查 Servlet 是否支持异步处理。 |
void startAsync() |
启动异步处理。 |
void startAsync(ServletRequest request, ServletResponse response) |
以指定请求和响应启动异步处理。 |
以下是 HttpServlet
类的一些重要特点:
继承关系:HttpServlet
继承自 GenericServlet
,因此它拥有 GenericServlet
中的一些通用功能,但它专门用于处理 HTTP 请求和响应。
提供 HTTP 请求处理方法:HttpServlet
提供了针对不同 HTTP 请求方法(GET、POST、PUT、DELETE 等)的处理方法,这些方法可以被子类重写来处理特定的请求类型。
默认实现:HttpServlet
中这些 HTTP 请求处理方法默认实现是返回 HTTP 状态码 405(Method Not Allowed),表示不支持对应的请求方法。因此,子类需要根据需要重写这些方法以提供自定义的请求处理逻辑。
获取请求信息:HttpServletRequest
参数提供了访问客户端 HTTP 请求信息的方法,如获取请求 URL、请求参数、请求头、会话信息等。
设置响应信息:HttpServletResponse
参数提供了设置 HTTP 响应内容、响应状态码、响应头等的方法。
由于 HttpServlet
是一个抽象类,不能直接实例化,开发者需要编写具体的子类来继承 HttpServlet
,然后实现相应的请求处理方法,根据业务逻辑处理不同类型的 HTTP 请求,并生成相应的 HTTP 响应。
HttpServletRequest
是Java Servlet API 中的一个接口,用于表示客户端发起的 HTTP 请求。它提供了访问客户端请求信息的方法,开发者可以通过该接口获取请求的各种属性、参数、头信息等。该接口继承自 ServletRequest
接口,专门用于处理 HTTP 请求。
以下是一些常用的 HttpServletRequest
接口的方法:
1. 获取请求信息
方法 | 说明 |
---|---|
String getMethod() |
获取 HTTP 请求方法,例如 GET、POST、PUT 等。 |
String getRequestURI() |
获取请求的 URI(Uniform Resource Identifier)部分,即去除协议、主机、端口号后的路径部分。 |
String getQueryString() |
获取请求的查询字符串,即 URL 中 “?” 后面的部分。 |
String getProtocol() |
获取请求使用的协议,如 “HTTP/1.1”。 |
String getContextPath() |
获取应用程序的上下文路径(Context Path),即应用程序的根路径。 |
2. 获取请求参数
方法 | 说明 |
---|---|
String getParameter(String name) |
获取指定名称的请求参数值。 |
Enumeration |
获取所有请求参数名称的枚举。 |
String[] getParameterValues(String name) |
获取指定名称的请求参数值数组,用于处理同名参数。 |
3. 获取请求头信息
方法 | 说明 |
---|---|
String getHeader(String name) |
获取指定名称的请求头信息。 |
Enumeration |
获取所有请求头名称的枚举。 |
4. 获取会话信息
方法 | 说明 |
---|---|
HttpSession getSession() |
获取与该请求关联的 HttpSession 对象,如果没有会话,则创建一个新的会话。 |
HttpSession getSession(boolean create) |
根据指定的 create 参数决定是否创建新的会话。true 代表创建,false 代表不创建。 |
5. 获取客户端信息
方法 | 说明 |
---|---|
String getRemoteAddr() |
获取客户端的 IP 地址。 |
String getRemoteHost() |
获取客户端的主机名。 |
int getRemotePort() |
获取客户端连接的端口号。 |
6. 其他常用方法
方法 | 说明 |
---|---|
Cookie[] getCookies() |
获取客户端发送的所有 Cookie。 |
Locale getLocale() |
获取客户端的首选语言环境。 |
Enumeration |
获取客户端支持的所有语言环境的枚举。 |
通过使用 HttpServletRequest
接口提供的这些方法,开发者可以在 Servlet 中获取请求的各种信息,从而更好地处理和响应客户端的请求。
创建一个 ShowHeaderServlet
类, 用于获取请求信息并返回响应。它可以响应 HTTP GET 请求,并将请求的一些信息以及请求头信息返回到客户端。
@WebServlet("/show")
public class ShowHeaderServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf8");
StringBuilder respBody = new StringBuilder();
respBody.append(req.getProtocol());
respBody.append("
");
respBody.append(req.getMethod());
respBody.append("
");
respBody.append(req.getRequestURL());
respBody.append("
");
respBody.append(req.getContextPath());
respBody.append("
");
respBody.append(req.getQueryString());
respBody.append("
");
respBody.append(" headers: "
);
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
respBody.append(headerName).append(": ").append(req.getHeader(headerName));
respBody.append("
");
}
resp.getWriter().write(respBody.toString());
}
}
访问服务器,可以发现响应内容如下:
创建一个 GetParameterServlet
类用于获取 GET 请求中的参数。
GET 方法的查询字符串说明:
- 在 HTTP 请求中,GET 方法通过将参数附加到 URL 的查询字符串(Query String)部分来传递数据。查询字符串是在 URL 的问号(?)后面的部分,用于向服务器传递键值对参数。它的格式通常为:
http://example.com/path/to/resource?key1=value1&key2=value2&key3=value3...
代码如下:
@WebServlet("/getParameter")
public class GetParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 期待收到的请求:http://localhost:8080/hello_servlet/getParameter?studentId=10&classId=20
resp.setContentType("text/html; charset=utf8");
String studentId = req.getParameter("studentId");
String classId = req.getParameter("classId");
resp.getWriter().write("studentId: " + studentId + ", classId: " + classId);
}
}
1. 首先约定前后端的交互接口
POST /postParameter
2. 前端创建页面testPost.html
,里面创建一个 form
表单
<form action="postParameter" method="post">
<input type="text" name="studentId">
<input type="text" name="classId">
<input type="submit" value="提交">
form>
3. 后端创建一个 PostParameterServlet
类,用于接收 form
表单提交的数据
@WebServlet("/postParameter")
public class PostParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf8");
String studentId = req.getParameter("studentId");
String classId = req.getParameter("classId");
resp.getWriter().write("studentId: " + studentId + ", classId: " + classId);
}
}
4. 访问服务器页面,然后提交参数
5. 提交数据后会自动跳转至响应页面
6. 通过 Fiddler
工具抓包,可以发现通过 POST
方法传递的参数在 body
中
1. 约定前后端交互接口
POST /jsonPostParameter
2. 后端获取 body
并通过 Jackson
解析 JSON 字符串
在Java代码中,需要引入
Jackson
这个库,完成对 JSON 的解析工作:
创建一个JsonPostServlet
类:
class Student {
public String studentId;
public String classId;
}
@WebServlet("/jsonPostParameter")
public class JsonPostParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
Student student = objectMapper.readValue(req.getInputStream(), Student.class);
resp.getWriter().write("studentId: " + student.studentId + ", classId: " + student.classId);
}
}
其中,Student
类用于储存 JSON 解析出来的数据。
3. 启动服务器,然后通过 Postman 工具发送 JSON 格式的数据
HttpServletResponse
也是 Java Servlet API 中的一个接口,它表示用于发送 HTTP 响应给客户端的对象。通过这个接口,Servlet 可以设置响应的内容、响应头、状态码等,以便向客户端发送适当的响应。
下面是 HttpServletResponse
接口的核心方法及其说明:
1. 设置响应头和状态码
方法 | 说明 |
---|---|
void setStatus(int sc) |
设置响应的状态码。 |
void setStatus(int sc, String sm) |
设置响应的状态码和描述信息。 |
int getStatus() |
获取响应的状态码。 |
String getHeader(String name) |
获取指定名称的响应头的值。 |
Collection |
获取所有响应头名称的集合。 |
Collection |
获取指定名称的响应头的所有值的集合。 |
void setHeader(String name, String value) |
设置指定名称的响应头的值。 |
void addHeader(String name, String value) |
添加指定名称的响应头的值(可以多次调用)。 |
void setIntHeader(String name, int value) |
设置指定名称的整数响应头的值。 |
void addIntHeader(String name, int value) |
添加指定名称的整数响应头的值(可以多次调用)。 |
2. 设置响应内容
方法 | 说明 |
---|---|
PrintWriter getWriter() |
获取用于发送字符文本的 PrintWriter 对象。 |
ServletOutputStream getOutputStream() |
获取用于发送二进制数据的 ServletOutputStream 对象。 |
void setContentLength(int len) |
设置响应内容的长度。 |
void setContentType(String type) |
设置响应内容的 MIME 类型。 |
String getContentType() |
获取响应内容的 MIME 类型。 |
void setCharacterEncoding(String charset) |
设置响应内容的字符编码。 |
3. 缓存控制
方法 | 说明 |
---|---|
void setDateHeader(String name, long date) |
设置指定名称的日期型响应头的值。 |
void addDateHeader(String name, long date) |
添加指定名称的日期型响应头的值(可以多次调用)。 |
void setExpires(long expires) |
设置响应的过期时间。 |
void addCookie(Cookie cookie) |
添加一个 Cookie 到响应中。 |
4. 重定向和错误处理
方法 | 说明 |
---|---|
void sendRedirect(String location) |
重定向到另一个 URL 。 |
void sendError(int sc) |
发送一个错误状态码。 |
void sendError(int sc, String msg) |
发送一个错误状态码和描述信息。 |
void reset() |
重置响应对象,清除所有设置的响应数据。 |
5. 其他方法
方法 | 说明 |
---|---|
void setBufferSize(int size) |
设置响应缓冲区的大小。 |
int getBufferSize() |
获取响应缓冲区的大小。 |
void flushBuffer() |
强制将缓冲区中的数据发送到客户端。 |
boolean isCommitted() |
检查是否已经提交响应。 |
void resetBuffer() |
重置响应缓冲区。 |
void setLocale(Locale loc) |
设置响应内容的区域设置。 |
这些方法提供了处理 Servlet 响应的各种操作和设置,开发人员可以根据需要使用这些方法来构建定制化的 HTTP 响应。
创建一个 StutasServlet
类,用于接收一个状态码参数,然后在响应中设置该状态码:
@WebServlet("/status")
public class StatusServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 期待收到请求:http://localhost:8080/hello_servlet/status?status=200
String statusStr = req.getParameter("status");
int status = Integer.parseInt(statusStr);
resp.setStatus(status);
resp.getWriter().write("status: " + status);
}
}
创建一个 AutoRefreshServlet
类,当浏览器访问时会每隔一秒自动刷新一次页面,并显示当前的时间戳。
@WebServlet("/autoRefresh")
public class AutoRefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("refresh", String.valueOf(1));
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
resp.getWriter().write("timestamp: " + timestamp.toString());
}
}
其中,通过 HTTP 响应报头中的 Refresh
字段,可以控制浏览器自动刷新的时机。
创建一个RedirectServlet
类,返回一个重定向 HTTP 请求,自动跳转到另一个页面。
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("https://csdn.net");
}
}
启动程序,通过 URL: http://localhost:8080/hello_servlet/redirect
访问,可以看到页面自动跳转到 CSDN 主页了。
通过 Fiddler
抓包可以看到,响应设置了 302 重定向状态码,重定向的位置就是 https://csdn.net
:
Cookie:
Set-Cookie
标头发送一个或多个 Cookie 到客户端,客户端会将这些 Cookie 存储在本地。Cookie
标头内,从而将相关数据传递给服务器。Session:
唯一的会话标识
(通常是一个长随机字符串),并将其存储在 Cookie 中,发送到客户端。服务器在同一时刻一般都会收到很多请求,因此就需要区分清楚哪一个请求是属于哪一个用户(客户端)的,所有就要求服务器与每一个用户都维持一个会话
,并且这个会话中包含了这个用户的唯一标识以及与用户信息的对应关系。
因此,会话的本质就是一个 “哈希表”,其中的key就是对用户的唯一标识(sessionId),value就是客户端信息(可以根据需求灵活设置)。
服务端与客户端维持会话的基本流程:
sessionId
通过 Set-Cookie
字段返回给客户端,然后将其保存在浏览器中。sessionId
通过 Cookie
字段发送给服务器。sessionId
在 Session 中找到与这个用户对应的用户信息,维持与该用户的会话,并处理请求任务。另外,Servlet 中的 Session 默认是保存到内存中的,那么就意味着:如果重启服务器则 Session 数据就会消失。为了避免这个问题,可以使用其他方式存储 Session 数据,如数据库或分布式缓存。
在用户登录案例中,通常的流程如下:
- 用户在登录页面输入用户名和密码,点击登录按钮提交表单。
- 服务器接收表单提交的数据,验证用户身份。
- 如果验证成功,服务器创建一个 Session,将用户信息存储在其中,并生成一个会话标识(Session ID)。
- 服务器通过
Set-Cookie
响应头将会话标识发送给客户端,存储在客户端的 Cookie 中。- 客户端在后续的每个请求中自动包含会话标识的 Cookie,以便服务器识别用户。
- 服务器通过会话标识从会话存储中取出用户信息,识别用户身份,实现用户的登录状态。
以下是简单的登录案例:
这个代码中主要是通过 HttpSession
类完成,并不需要我们手动操作 Cookie 对象。
LoginServlet
类,用于实现登录逻辑。@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
resp.setContentType("text/html; charset=utf8");
// 1. 获取输入的用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
// 2. 判断用户名和密码是否合法
if(!username.equals("admin") || !password.equals("123456")){
resp.getWriter().write("用户名或密码错误!");
return;
}
// 3. 登录成功,设置 Session
HttpSession session = req.getSession(true); // Session 不存在则创建
session.setAttribute("username", username);
session.setAttribute("loginCount", 0);
// 4. 重定向到 index
resp.sendRedirect("index");
}
}
IndexServlet
类,用于判断用户是否登录,如果登录则显示用户信息。@WebServlet("/index")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
resp.setContentType("text/html; charset=utf8");
// 1. 获取 Session,判断用户是否登录
HttpSession session = req.getSession(false); // Session 不存在也不创建
if(null == session){
// 用户没有登录,重定向到 login.html
resp.sendRedirect("login.html");
return;
}
// 2. 已经登录,获取 Session 中保存的用户名和登录次数
String username = (String)session.getAttribute("username");
Integer loginCount = (Integer) session.getAttribute("loginCount");
loginCount += 1;
session.setAttribute("loginCount", loginCount);
// 3. 展示用户信息到页面上
StringBuilder respBody = new StringBuilder();
respBody.append(String.format("用户名:%s", username));
respBody.append(String.format("loginCount: %d", loginCount));
resp.getWriter().write(respBody.toString());
}
}
webapp
目录下创建 login.html
,用于提交登录用户名和密码。<form action="login" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="登录">
form>
访问 http://localhost:8080/hello_servlet/index
,由于没有登录,跳转至 login.html
:
正确输入用户名和密码:
成功登录,跳转至 index
,成功显示 Session 中设置的用户信息:
文件上传是 Web 开发中常见的操作,Java Servlet 提供了处理文件上传的支持。在 Java Servlet 中,文件上传通常使用 javax.servlet.http.Part
类或 javax.servlet.http.HttpServletRequest
的相关方法来实现。
HttpServletRequest
接口相关方法:
方法 | 说明 |
---|---|
Part getPart(String name) |
获取请求中给定的 name 文件 |
Collection |
获取所有文件 |
Part
类相关方法:
方法 | 说明 |
---|---|
String getSubmittedFileName() |
获取提交的文件名 |
String getContentType() |
获取提交的文件类型 |
long getSize() |
获取文件的大小 |
void write(String path) |
把提交的文件数据写入磁盘文件 |
1. 在 webapp
目录下,创建 upload.html
用于提交图片文件。
<body>
<form action="upload" enctype="multipart/form-data" method="post">
<input type="file" name="image">
<input type="submit" value="提交图片">
form>
body>
上传文件一般通过 POST 请求的 form
表单实现,并且在 form
标签中要加上 multipart/form-data
字段。
2. 创建 UploadServlet
类,用于处理客户端提交图片的请求。
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取图片文件
Part image = req.getPart("image");
// 2. 打印图片信息
System.out.println("图片名称:" + image.getSubmittedFileName());
System.out.println("图片类型:" + image.getContentType());
System.out.println("图片大小:" + image.getSize());
// 3. 将图片保存到磁盘
image.write("D:/image/tomcat.jpg");
resp.getWriter().write("upload OK!");
}
}
注意事项:
- 需要给
UploadServlet
加上@MultipartConfig
注解,否则服务器代码无法使用getPart
方法。getPart
的参数需要和form
表单中input
标签的name
属性对应。- 客户端一次可以提交多个文件(使用多个
input
标签),此时服务器可以通过getParts
获取所有的Part
对象。
3. 运行程序,并提交一张图片。
此时发现 IDEA 控制台打印的信息:
D盘的 image
目录下多了一张图片: