Servlet 简介
什么是 Servlet
Servlet 是一种服务器端的 Java 技术,设计用于扩展 Web 服务器或应用服务器的功能。Servlet 主要运行在服务器端,用来处理来自客户端的请求并生成响应。它们是 Java 技术中处理 HTTP 请求和响应的核心组件之一。
servlet__8">servlet 的工作过程
- 客户端发起请求:用户通过浏览器或其他 HTTP 客户端发送一个 HTTP 请求到 Web 服务器。
- Web 容器接收请求:Web 容器(如 Apache Tomcat、Jetty 等)接收到 HTTP 请求后,根据请求中的 URL 信息决定哪个 Servlet 应该负责处理该请求。
- 调用 Servlet 实例:如果相应的 Servlet 尚未加载,则 Web 容器会先创建 Servlet 实例,并调用其
init()
方法进行初始化;如果 Servlet 已经被加载,则跳过此步骤。 - 处理请求:Web 容器将请求分派给对应的 Servlet 实例,然后调用它的
service()
方法。在这个方法里,Servlet 会根据请求类型(GET, POST 等)来选择适当的方法(例如doGet()
,doPost()
等)来处理请求。 - 生成响应:Servlet 处理完请求后,构建响应内容(如 HTML 页面、JSON 数据等),并通过响应对象返回给客户端。
- 销毁 Servlet:当 Web 应用程序关闭或重新部署时,Web 容器会调用 Servlet 的
destroy()
方法释放资源。
Servlet 特点
- 持久性:Servlet 一旦被加载就保持在内存中,直到服务器关闭或应用重启,这使得 Servlet 可以高效地处理多个请求。
- 线程安全:由于每个请求都是在一个独立的线程中处理的,因此需要考虑多线程环境下的线程安全问题。
- 灵活性:可以通过配置文件或者注解的方式来配置 Servlet 的行为和映射规则。
- 安全性:支持各种认证和授权机制,以保护对 Servlet 的访问。
- 可移植性:基于 Java 语言编写,Servlet 可以在任何支持 Java 的平台上运行。
Servlet 的关键点
- 平台无关性:Servlet 是用 Java 编写的,因此它们具有 Java 语言的所有优点,包括跨平台能力。
- 多线程:Servlet 由 Web 容器(如 Apache Tomcat)管理,容器会为每个请求创建一个新线程,并将请求分派给 Servlet 实例。这使得 Servlet 能够处理多个并发请求。
- 生命周期:Servlet 的生命周期由 Web 容器控制,主要包括初始化(init)、服务(service)和销毁(destroy)三个阶段。
- HTTP 协议支持:虽然 Servlet 可以处理多种类型的请求,但它们最常用于处理 HTTP 请求。为此,Java Servlet API 提供了专门的类
HttpServlet
,它简化了对 HTTP GET 和 POST 请求的处理。 - 部署描述符:Servlet 的配置信息通常保存在一个名为
web.xml
的文件中,该文件位于 WEB-INF 目录下。从 Servlet 3.0开始,也支持使用注解来配置 Servlet。 - 安全性:可以通过安全约束和用户认证等机制来保护 Servlet 资源的安全。
- 状态管理:Servlet 可以通过使用会话跟踪机制(例如,通过 URL 重写、隐藏表单字段或 Cookies)来维护客户端的状态信息。
- 性能:因为 Servlet 实例在容器启动时加载并在内存中持久化,所以它们比 CGI 脚本更高效,后者每次请求都会创建新的进程。
- API:Java Servlet API 定义了一组接口和类,开发人员可以使用这些接口和类来创建自己的 Servlet。
- 框架集成:现代 Web 开发通常不会直接使用 Servlet,而是依赖于建立在其上的高级框架,比如 Spring MVC 或 Jakarta EE 中的 JSF,这些框架提供了更高层次的抽象,简化了 Web 应用程序的开发。
Servlet 快速开发 Hello World
使用 java servlet 快速开发 hello world,需要满足以下步骤:
- 准备 java 和 tomcat 环境。
- 如果使用 maven 构建项目,需要导入 servlet 依赖。如果创建 javaWeb 项目,需要引入 tomcat jar包。
- 编写 servlet 类:创建一个新的 java 类,并让它继承自
HttpServlet
。覆盖doGet()
或doPost()
方法来处理 HTTP GET 或 POST 请求。
java">import java.io.*;
import javax.servlet.http.*;public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();out.println("<h1>Hello World! </h1>");}
}
- 在
WEB-INF/web.xml
中添加:
<servlet><servlet-name>HelloServlet</servlet-name><servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>HelloServlet</servlet-name><url-pattern>/hello</url-pattern>
</servlet-mapping>
-
部署到 servlet 容器,有两种方法:
-
使用 tomcat:将项目打包成
WAR
文件,放到 tomcat 的webapps
目录下。 -
IDE 直接运行(如 IntelliJ 或 Eclipse):配置本地 tomcat 并启动。
-
-
启动服务器并测试:打开浏览器,输入 http://localhost:8080/你的项目名/hello,你应该能看到页面显示“Hello, World!”
servlet__94">servlet 映射注意事项
- 一个
servlet-name
可以同时对应多个url-pattern
。
<servlet-mapping><servlet-name>HelloServlet</servlet-name><url-pattern>/hello1</url-pattern><url-pattern>/hello2</url-pattern>
</servlet-mapping>
url-pattern
匹配规则:
- 精确匹配:
/servlet
。 - 模糊匹配:
*
作为通配符,*
在哪里,哪里就是模糊的。/
匹配全部,不包含 jsp 文件。/*
匹配全部,包含 jsp 文件。/xxx/*
匹配前缀,后缀模糊。*.xxx
匹配后缀,前缀模糊。
servlet__120">servlet 注解方式配置
servlet 映射可以通过在 servlet 类上加上注解 @WebServlet
进行配置。
java">import java.io.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();out.println("<h1>Hello World! </h1>");}
}
这样就可以不用编写 web.xml
文件了
servlet__146">servlet 的生命周期
生命周期简介
应用程序中的对象不仅在空间上有层次结构的关系,在时间上也会因为处于程序运行过程中的不同阶段而表现出不同状态和不同行为——这就是对象的生命周期。
简单来说,就是对象在容器中从开始创建到销毁的过程。
servlet__156">servlet 容器
servlet 对象是 servlet 容器创建的,生命周期方法都是由容器(目前是 tomcat)调用的,这一点和之前所编写的代码有很大不同,越往后面到框架,越来越多的对象就要交给容器或框架来创建,越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上。
servlet__162">servlet 的生命周期
servlet 的生命周期由其在 web 容器(如 Apache Tomcat)中的管理来定义,主要包括以下几个阶段:加载和实例化、初始化、服务请求以及销毁。以下是 servlet 生命周期的详细描述:
- 加载和实例化
当 web 容器接收到对特定 servlet 的第一个请求时,它会加载该 servlet 类并创建其实例。这个过程可以通过 web 容器自动完成,也可以通过配置让容器在启动时就加载某些 servlet。
- 注解方式:使用
@WebServlet
注解指定 url 映射。 - 部署描述符(web.xml):在
web.xml
文件中配置 servlet 及其映射关系。
- 初始化(init)
一旦 servlet 实例被创建,容器就会调用其 init()
方法进行初始化。此方法只调用一次,用于执行一次性设置操作,比如建立数据库连接或初始化资源。
java">public void init() throws ServletException {// 初始化代码
}
注意事项:
- 默认情况下,第一次收到请求时容器创建 servlet 实例并初始化。
- 通过配置
loadOnStartup
,可在容器启动时立即初始化。
配置方式:
java">@WebServlet(urlPatterns = "/hello", loadOnStartup = 1) // 数字越小优先级越高
或在 web.xml
中配置:
<servlet><servlet-name>HelloServlet</servlet-name><servlet-class>HelloServlet</servlet-class><load-on-startup>1</load-on-startup>
</servlet>
如果要自己配置时,建议从6开始配置,因为 tomcat 默认的 web.xml
中已经配置到了5。
- 服务(service)
初始化完成后,servlet 就可以处理客户端请求了。每当有请求到达时,容器都会调用 servlet 的 service()
方法,并根据 http 请求类型(GET, POST 等),将请求分派给相应的 doGet()
, doPost()
等方法。
java">protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 处理GET请求的代码
}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 处理POST请求的代码
}
- 销毁(destroy)
当 web 应用被关闭或者重新部署时,容器会调用 servlet 的 destroy()
方法,允许 servlet 释放占用的资源,如关闭数据库连接等。之后,servlet 对象会被垃圾回收。
java">public void destroy() {// 清理代码
}
生命周期总结:
- 加载和实例化:web 容器加载 servlet 类并创建其实例。
- 初始化:容器调用
init()
方法进行必要的初始化工作。 - 服务:容器调用
service()
方法处理客户端请求,根据请求类型调用不同的方法(如doGet()
,doPost()
)。 - 销毁:当 web 应用停止或重载时,容器调用
destroy()
方法清理资源,随后 servlet 对象将被销毁。
了解 servlet 的生命周期对于有效地编写和调试 servlet 应用程序非常重要,因为它影响着资源的分配与释放、性能优化等方面。正确地实现这些生命周期方法可以帮助你更好地管理 servlet 的行为和状态。
注意事项
DefaultServlet
打开 tomcat 下的 conf 目录下的 web.xml
文件可以看到:
<servlet><servlet-name>default</servlet-name><servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class><init-param><param-name>debug</param-name><param-value>0</param-value></init-param><init-param><param-name>listings</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
DefaultServlet
主要用于处理对静态资源的请求,比如 HTML 页面、图片、CSS 文件和 JavaScript 文件等。当部署的应用程序中的其他 Servlet 不处理某个请求时,就会由 DefaultServlet
来处理这些静态内容。
功能
- 提供静态资源:
DefaultServlet
能够高效地从应用的目录结构中提供静态文件。 - 支持范围请求:允许客户端请求部分文件内容,这对于大文件下载或断点续传功能特别有用。
- 目录浏览:如果启用了目录浏览功能,当访问一个目录而非具体文件时,
DefaultServlet
可以生成并返回该目录下的文件列表页面。 - 缓存控制:通过设置响应头来控制浏览器缓存行为,提高性能。
servlet__284">servlet 继承结构
Servlet 接口
java">public interface Servlet {void init(ServletConfig var1) throws ServletException;ServletConfig getServletConfig();void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;String getServletInfo();void destroy();
}
servlet 接口是 java servlet API 的核心部分,定义了所有 servlet 必须实现的方法。尽管大多数时候你会继承 HttpServlet
类而不是直接实现 servlet 接口(因为 HttpServlet
已经实现了 servlet 接口,并提供了便捷的方法来处理 HTTP 请求),了解 servlet 接口定义的基本方法对于理解 servlet 的工作原理是非常有帮助的。以下是 servlet 接口中定义的主要方法:
void init(ServletConfig config)
- 用途:当 servlet 被 Web 容器加载并实例化后,会调用此方法进行初始化。这个方法只会被调用一次。
- 参数:
ServletConfig
对象包含 servlet 的配置信息和初始化参数。 - 注意:通常情况下,你不需要直接实现这个方法,除非你需要执行特定的初始化操作。
void service(ServletRequest req, ServletResponse res)
- 用途:这是 servlet 的核心方法,每当收到客户端请求时都会调用此方法来处理请求并生成响应。
- 参数:
ServletRequest
:封装了客户端请求的所有信息。ServletResponse
:提供给 servlet 用来返回响应给客户端的方式。
- 注意:直接实现 servlet 接口时需要重写此方法来处理不同类型的请求。但是,如果你继承自
HttpServlet
,通常不需要直接覆盖service()
方法,而是根据请求类型(GET、POST 等)重写相应的方法(如doGet()
,doPost()
等)。
void destroy()
ServletConfig getServletConfig()
String getServletInfo()
- 用途:返回关于 servlet 的信息字符串,比如作者、版本和版权等信息。
- 注意:虽然这不是必需实现的功能,但它可以为维护人员提供有用的信息。
实现细节
虽然可以直接实现 servlet 接口,但在实际开发中,更常见的是扩展 HttpServlet
抽象类,因为它简化了许多任务,特别是处理 HTTP 请求的过程。HttpServlet
已经实现了上述方法,并提供了额外的方法如 doGet()
, doPost()
等,专门用于处理 HTTP GET 和 POST 请求。
通过继承 HttpServlet
,你可以专注于重写适合你的应用需求的方法,而无需关心底层协议的具体实现细节。这样不仅提高了开发效率,也使得代码更加简洁易读。
GenericServlet 抽象类
GenericServlet
是一个抽象类,它实现了 Servlet
、ServletConfig
和 Serializable
接口,简化了直接实现 Servlet
接口的复杂度。尽管 GenericServlet
可以用于任何类型的协议,但它通常被设计为与 HTTP 协议无关的基础类。对于开发基于 HTTP 的应用,更常用的是它的子类 HttpServlet
。
主要特点
- 通用性:
GenericServlet
提供了一个不依赖于任何特定协议的框架,可以用来创建非 HTTP servlet。 - 简化配置管理:通过实现
ServletConfig
接口,GenericServlet
提供了对初始化参数的便捷访问方法。 - 生命周期管理:实现了
init()
方法,使得开发者只需重写service()
方法来处理请求,而不需要担心其他生命周期方法的具体实现。
核心方法
init(ServletConfig config)
- 调用超类的
init(config)
来保存配置对象,并允许子类进行自定义的初始化操作。
- 调用超类的
init()
- 一个无参的
init()
方法,被上述带参数的init(ServletConfig config)
自动调用。如果需要执行自定义的初始化逻辑,可以覆盖此方法。
- 一个无参的
service(ServletRequest req, ServletResponse res)
- 必须由子类实现的方法,用于处理客户端请求。该方法接收一个
ServletRequest
对象和一个ServletResponse
对象作为参数,分别代表请求和响应。
- 必须由子类实现的方法,用于处理客户端请求。该方法接收一个
getServletConfig()
- 返回
ServletConfig
对象,该对象包含了 servlet 的初始化参数。
- 返回
getServletContext()
- 返回
ServletContext
对象,提供了一种与整个 Web 应用程序通信的方式。
- 返回
getServletInfo()
- 返回关于 servlet 的信息字符串,比如作者、版本等。默认返回空字符串,可以根据需要覆盖。
destroy()
- 当 servlet 即将被卸载时,容器会调用此方法,以便释放资源。默认为空实现,可以根据需要添加清理代码。
虽然 GenericServlet
提供了一些便利,但在实际开发中,特别是针对 Web 应用,更多时候会选择继承 HttpServlet
类,因为它提供了更具体的支持来处理 HTTP 请求(如GET、POST等),从而减少了手动解析协议的需求。然而,了解 GenericServlet
对于理解 Servlet API 的基本机制仍然很有帮助。
HttpServlet 抽象类
HttpServlet
是一个抽象类,它是 GenericServlet
的子类,专门设计用于处理基于 HTTP 协议的请求。通过继承 HttpServlet
类,开发者可以更容易地编写处理 HTTP GET、POST 等请求的 servlet。HttpServlet
提供了便捷的方法来处理这些常见的 HTTP 请求方法,使得开发人员不需要直接操作低级别的 ServletRequest
和 ServletResponse
对象。
主要特点
- 专注于 HTTP:
HttpServlet
专为 HTTP 协议设计,提供了针对 HTTP 请求(如 GET、POST 等)的特定方法。 - 简化开发:通过提供预定义的方法如
doGet()
,doPost()
等,简化了 HTTP 请求的处理过程。 - 扩展性强:虽然
HttpServlet
是抽象类,但其提供的默认实现足以应对很多场景,只需根据需要覆盖相应的方法即可。
核心方法
尽管 HttpServlet
包含多个方法,但对于大多数应用而言,最常被重写的方法包括:
doGet(HttpServletRequest req, HttpServletResponse resp)
- 处理 HTTP GET 请求。通常用于从服务器检索信息而不改变服务器状态。
doPost(HttpServletRequest req, HttpServletResponse resp)
- 处理 HTTP POST 请求。适用于向服务器发送数据并可能改变服务器状态的情况,例如提交表单。
doPut(HttpServletRequest req, HttpServletResponse resp)
- 处理 HTTP PUT 请求。通常用于上传内容到指定的URL,如果资源不存在,则创建它。
doDelete(HttpServletRequest req, HttpServletResponse resp)
- 处理 HTTP DELETE 请求。用来删除指定的资源。
doHead(HttpServletRequest req, HttpServletResponse resp)
- 处理 HTTP HEAD 请求。类似于 GET 请求,但只返回响应头,不返回响应体。
doOptions(HttpServletRequest req, HttpServletResponse resp)
- 处理 HTTP OPTIONS 请求。允许客户端查看目标资源支持的通信选项。
doTrace(HttpServletRequest req, HttpServletResponse resp)
- 处理 HTTP TRACE 请求。回显收到的请求,主要用于诊断。
通过继承 HttpServlet
抽象类,开发者可以获得对 HTTP 请求的更高级别的支持,而无需直接处理底层的 ServletRequest
和 ServletResponse
接口。这不仅简化了代码,也提高了开发效率。对于大多数 Web 应用程序来说,这是处理 HTTP 请求的标准方式。
servlet_413">自定义 servlet
如果要自定义 servlet,通常会继承 HttpServlet
类,然后重写 service()
或 doGet() 和 doPost()
方法,比较推荐去重写 doGet() 和 doPost()
方法,因为父类 service()
可能会做了一些处理,如果我们重写的话,父类的方法就失效了。
如果 doGet()
和 doPost()
方法体相同,可以在其中一个方法体里直接调用另一个方法。
ServletConfig 和 ServletContext
ServletConfig
ServletConfig
是一个接口,它提供了获取 servlet 初始化参数和 servlet 配置信息的方法。每个 servlet 实例在初始化时都会被赋予一个 ServletConfig
对象,通过这个对象,servlet 可以访问在部署描述符(如 web.xml
)中为该 servlet 定义的初始化参数以及其他配置信息,每个 servlet 都有自己独立唯一的 ServletConfig
对象。
主要用途
- 初始化参数:允许开发者为特定的 servlet 设置初始化参数,这些参数可以在 servlet 的整个生命周期内使用。
ServletContext
访问:提供了一种方式来访问ServletContext
,这使得 servlet 能够与整个 Web 应用进行交互,比如共享数据、获取资源路径等。
核心方法
以下是 ServletConfig
接口中定义的一些重要方法:
getInitParameter(String name)
- 返回指定名称的初始化参数值。如果不存在这样的参数,则返回
null
。
- 返回指定名称的初始化参数值。如果不存在这样的参数,则返回
getInitParameterNames()
- 返回一个
Enumeration<String>
对象,包含所有初始化参数的名字。如果没有初始化参数,则返回空的枚举。
- 返回一个
getServletName()
getServletContext()
- 返回代表当前上下文环境的
ServletContext
对象,通过它可以执行跨多个 servlet 共享的操作。
- 返回代表当前上下文环境的
代码示例
下面是一个如何在自定义 servlet 中使用 ServletConfig
获取初始化参数的例子:
自定义 servlet:
java">import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class MyServlet extends HttpServlet {private ServletConfig servletConfig;@Overridepublic void init(ServletConfig config) throws ServletException {super.init(config);this.servletConfig = config;}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取单个初始化参数String paramValue = servletConfig.getInitParameter("myParam");response.getWriter().println("Parameter value: " + paramValue);// 列出所有初始化参数Enumeration<String> parameterNames = servletConfig.getInitParameterNames();while (parameterNames.hasMoreElements()) {String paramName = parameterNames.nextElement();String paramValueAll = servletConfig.getInitParameter(paramName);response.getWriter().println("Parameter Name: " + paramName + ", Value: " + paramValueAll);}// 获取servlet名称String servletName = servletConfig.getServletName();response.getWriter().println("Servlet Name: " + servletName);}
}
为了使上述代码工作,你需要在 web.xml
文件中为你的 servlet 添加初始化参数:
web.xml
:
java"><servlet><servlet-name>MyServlet</servlet-name><servlet-class>com.example.MyServlet</servlet-class><init-param><param-name>myParam</param-name><param-value>valueForMyParam</param-value></init-param><init-param><param-name>anotherParam</param-name><param-value>valueForAnotherParam</param-value></init-param>
</servlet><servlet-mapping><servlet-name>MyServlet</servlet-name><url-pattern>/myservlet</url-pattern>
</servlet-mapping>
这样,当你访问 /myservlet
时,servlet 将能够读取并显示其初始化参数的值。使用 ServletConfig
接口可以让你更灵活地管理和使用 servlet 的初始化参数,同时也能更好地组织和维护你的 web 应用程序。
ServletContext
ServletContext
是 servlet API 中的一个接口,代表了整个 Web 应用的上下文环境。每个 Java Web 应用程序在启动时都会创建一个 ServletContext
实例,这个实例在整个应用的生命周期内都可用,并且可以被该应用中的所有 servlet 共享。通过 ServletContext
,servlet 可以访问应用级别的配置信息、共享数据以及资源文件等。
总结:
ServletContext
对象有称呼为上下文对象,或者叫应用域对象。- 容器会为每个 app 创建一个独立的唯一的
ServletContext
对象。 ServletContext
对象为所有的 servlet 所共享。ServletContext
可以为所有的 servlet 提供初始配置参数。
主要功能
- 共享数据:允许不同的 servlet 之间共享信息。
- 获取初始化参数:可以读取在
web.xml
或者注解中定义的应用级别初始化参数。 - 资源管理:能够获取应用内的资源文件(如配置文件)。
- 日志记录:提供基本的日志记录功能。
- 分发请求:支持请求转发和包含其他资源的功能。
- 属性操作:可以在
ServletContext
上设置、获取或移除属性,这些属性可以在整个Web应用范围内共享。
核心方法
以下是 ServletContext
接口中一些常用的方法:
getAttribute(String name)
和setAttribute(String name, Object object)
- 获取和设置应用范围内的属性值。
getInitParameter(String name)
和getInitParameterNames()
- 获取应用级别的初始化参数及其名称集合。
getContextPath()
- 返回当前 Web 应用的上下文路径。
getResourceAsStream(String path)
- 返回指定路径下的资源输入流,便于读取文件内容。
getRequestDispatcher(String path)
- 返回一个
RequestDispatcher
对象,用于请求转发或包含其他资源。
- 返回一个
log(String msg)
和log(String message, Throwable throwable)
- 提供日志记录功能,前者记录简单消息,后者记录异常信息及堆栈跟踪。
代码示例
java">import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class ContextServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {ServletContext context = getServletContext();// 设置应用级属性context.setAttribute("appMessage", "Welcome to the application");// 获取应用级属性String appMessage = (String) context.getAttribute("appMessage");response.getWriter().println("Application Message: " + appMessage);// 获取初始化参数String initParam = context.getInitParameter("exampleParam");response.getWriter().println("Init Parameter: " + initParam);// 读取资源文件InputStream resourceStream = context.getResourceAsStream("/WEB-INF/config.properties");if (resourceStream != null) {response.getWriter().println("Resource loaded successfully.");} else {response.getWriter().println("Failed to load resource.");}}
}
web.xml:
<web-app><context-param><param-name>exampleParam</param-name><param-value>valueForExampleParam</param-value></context-param><servlet><servlet-name>contextServlet</servlet-name><servlet-class>com.example.ContextServlet</servlet-class></servlet><servlet-mapping><servlet-name>contextServlet</servlet-name><url-pattern>/contextServlet</url-pattern></servlet-mapping>
</web-app>
通过 ServletContext
,你可以实现跨多个 servlet 的资源共享和通信,这对于构建复杂的 Web 应用程序非常有用。它不仅增强了不同组件之间的协作能力,还简化了对全局配置和资源的管理。
其他重要 API
获取资源的真实路径
java">String realPath = servletContext.getRealPath("资源在web目录中的路径");
- 例如我们的目标是需要获取项目中某个静态资源的路径,不是工程目录中的路径,而是部署目录中的路径;我们如果直接拷贝其在我们电脑中的完整路径的话其实是有问题的,因为如果该项目以后部署到公司服务器上的话,路径肯定是会发生改变的,所以我们需要使用代码动态获取资源的真实路径。只要使用了
servletContext
动态获取资源的真实路径,那么无论项目的部署路径发生什么变化,都会动态获取项目运行时候的实际路径,所以就不会发生由于写死真实路径而导致项目部署位置改变引发的路径错误问题。
获取项目的上下文路径
java">String contextPath = servletContext.getContextPath();
- 项目的部署名称,也叫项目的上下文路径,在部署进入 tomcat 时所使用的路径,该路径是可能发生变化的,通过该 API 动态获取项目真实的上下文路径,可以帮助我们解决一些后端页面渲染技术或者请求转发和响应重定向中的路径问题。
域对象的相关API
- 域对象:一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同。
ServletContext
代表应用,所以ServletContext
域也叫作应用域,是 webapp 中最大的域,可以在本应用内实现数据的共享和传递。- webapp 中的三大域对象,分别是应用域、会话域、请求域。
- 三大域对象都具有的 API 如下:
API | 功能解释 |
---|---|
void setAttribute(String key,Object value); | 向域中存储/修改数据 |
Object getAttribute(String key); | 获得域中的数据 |
void removeAttribute(String key); | 移除域中的数据 |
HttpServletRequest
HttpServletRequest
是 servlet API 中的一个接口,它封装了客户端请求的所有信息。每当客户端向服务器发送一个请求时(例如通过点击链接、提交表单或输入 URL),Web 容器就会创建一个 HttpServletRequest
对象,并将其作为参数传递给被调用的 servlet 的 service()
方法或者其特定的 doGet()
, doPost()
等方法。
HttpServletRequest
是一个接口,其父接口是ServletRequest
。HttpServletRequest
是 Tomcat 将请求报文转换封装而来的对象,在 Tomcat 调用service()
方法时传入。HttpServletRequest
代表客户端发来的请求,所有请求中的信息都可以通过该对象获得。
主要功能
- 获取请求参数:可以从请求中提取查询字符串、表单数据等。
- 获取请求头信息:允许访问所有 HTTP 请求头。
- 会话管理:支持与客户端建立和维护会话。
- 路径信息:提供关于请求 URL 的信息,包括上下文路径、Servlet 路径和路径信息。
- Cookies:可以读取客户端发送的 cookie 信息。
- 属性操作:可以在请求范围内设置、获取或移除属性,这些属性仅在当前请求有效。
核心方法
以下是 HttpServletRequest
接口中一些常用的方法:
- 请求参数
getParameter(String name)
:返回指定名称的第一个请求参数值。getParameterValues(String name)
:当参数是多值(如复选框)时使用,返回一组值。getParameterMap()
:返回请求参数的映射,其中键是参数名,值是对应的参数值数组。
- 请求头信息
getHeader(String name)
:获取指定名称的请求头值。getHeaders(String name)
:返回指定名称的所有请求头值的枚举。getHeaderNames()
:返回所有请求头名的枚举。
- 会话管理
getSession()
和getSession(boolean create)
:获取当前会话,如果不存在且create
为true
则创建新会话。
- 路径信息
getContextPath()
:返回请求的上下文路径。getRequestURI()
:返回请求行中的资源名称部分。getQueryString()
:返回查询字符串(如果有)。
- Cookies
getCookies()
:返回所有发送到服务器的cookie对象的数组。
- 属性操作
setAttribute(String name, Object o)
:在请求范围内设置属性。getAttribute(String name)
:获取请求范围内的属性值。removeAttribute(String name)
:从请求中删除指定名称的属性。
代码示例
下面是一个简单的例子,展示了如何使用 HttpServletRequest
来处理表单提交的数据以及访问请求头信息:
java">import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/formHandler")
public class FormHandlerServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取表单参数String username = request.getParameter("username");String password = request.getParameter("password");// 输出表单参数response.setContentType("text/html;charset=UTF-8");response.getWriter().println("<h1>Form Data</h1>");response.getWriter().println("Username: " + username + "<br>");response.getWriter().println("Password: " + password + "<br>");// 获取请求头信息String userAgent = request.getHeader("User-Agent");response.getWriter().println("User-Agent: " + userAgent + "<br>");// 获取Cookie信息Cookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie cookie : cookies) {response.getWriter().println("Cookie Name: " + cookie.getName() + ", Value: " + cookie.getValue() + "<br>");}} else {response.getWriter().println("No cookies found.");}}
}
在这个例子中,我们创建了一个名为 FormHandlerServlet
的 servlet,它处理 POST 请求并展示如何从请求中提取表单数据、请求头信息以及 cookie 数据。这展示了 HttpServletRequest
在处理客户端请求时的强大功能。通过利用 HttpServletRequest
提供的各种方法,开发者可以方便地访问和处理来自客户端的所有相关信息。
HttpServletResponse
HttpServletResponse
是 Servlet API 中的一个接口,它代表了服务器对客户端请求的响应。通过 HttpServletResponse
对象,servlet 可以向客户端发送响应数据,包括设置响应头、状态码以及输出响应内容等。
HttpServletResponse
是一个接口,其父接口是ServletResponse
。HttpServletResponse
是 Tomcat 预先创建的,在 Tomcat 调用service()
方法时传入。HttpServletResponse
代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息。
主要功能
- 发送响应数据:可以通过
PrintWriter
或OutputStream
向客户端发送文本或二进制数据。 - 设置响应头:可以设置各种 HTTP 响应头,如内容类型、缓存控制等。
- 重定向:支持将请求重定向到另一个资源。
- 错误处理:允许发送错误状态码和自定义错误页面。
- 会话管理:可以通过 cookie 来维护与客户端的会话状态。
核心方法
以下是 HttpServletResponse
接口中一些常用的方法:
- 发送响应数据
getWriter()
:返回一个PrintWriter
对象,用于发送字符文本响应。getOutputStream()
:返回一个ServletOutputStream
对象,用于发送二进制数据。
- 设置响应头
setContentType(String type)
:设置响应的内容类型(如text/html
,application/json
)。addHeader(String name, String value)
:添加一个响应头。setHeader(String name, String value)
:设置一个响应头,如果该响应头已存在,则覆盖原有的值。
- 重定向
sendRedirect(String location)
:将客户端重定向到另一个资源(URL)。这会导致浏览器发出一个新的请求。
- 错误处理
setStatus(int sc)
:设置整数状态码。sendError(int sc)
和sendError(int sc, String msg)
:发送指定的状态码及可选的消息给客户端,并自动使用适当的错误页面。
- 会话管理
addCookie(Cookie cookie)
:向客户端添加一个新的 cookie。
代码示例
下面是一个简单的例子,展示了如何使用 HttpServletResponse
发送文本响应、设置响应头以及执行重定向:
java">import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/responseDemo")
public class ResponseDemoServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 设置响应内容类型response.setContentType("text/html;charset=UTF-8");// 发送文本响应response.getWriter().println("<h1>Hello from HttpServletResponse</h1>");// 添加响应头response.setHeader("MyCustomHeader", "HeaderValue");// 设置cookieCookie userCookie = new Cookie("username", "john_doe");userCookie.setMaxAge(60*60); // 有效期为一小时response.addCookie(userCookie);// 执行重定向// response.sendRedirect("http://example.com");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 处理POST请求doGet(request, response);}
}
在这个例子中,我们创建了一个名为 ResponseDemoServlet
的 servlet,它演示了如何使用 HttpServletResponse
设置响应的内容类型、发送文本响应、设置自定义响应头、添加 cookie 以及如何进行重定向(注释部分)。这些功能使得 HttpServletResponse
成为了与客户端通信的关键组件之一,允许开发者灵活地控制响应的各种方面。
请求转发和响应重定向
请求转发和响应重定向是 Web 开发中两种不同的机制,用于处理客户端请求并将用户导航到另一个资源。
-
请求转发和响应重定向是 web 应用中间接访问项目资源的两种手段,也是 Servlet 控制页面跳转的两种手段。
-
请求转发通过
HttpServletRequest
实现,响应重定向通过HttpServletResponse
实现。 -
请求转发生活举例:张三找李四借钱,李四没有,李四找王五,让王五借给张三。
-
响应重定向生活举例:张三找李四借钱,李四没有,李四让张三去找王五,张三自己再去找王五借钱。
请求转发
- 请求转发通过
HttpServletRequest
对象获取请求转发器实现。 - 请求转发是服务器内部的行为,对客户端是屏蔽的。
- 客户端只发送了一次请求,客户端地址栏不变。
- 服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源。
- 因为全程只有一个
HttpServletRequset
对象,所以请求参数可以传递,请求域中的数据也可以传递。 - 请求转发可以转发给其他 Servlet 动态资源,也可以转发给一些静态资源以实现页面跳转。
- 请求转发可以转发给 WEB-INF 下受保护的资源。
- 请求转发不能转发到本项目以外的外部资源。
代码示例
java">import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/original")
public class OriginalServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 添加请求属性request.setAttribute("message", "This is a forwarded message");// 获取RequestDispatcher并转发请求RequestDispatcher dispatcher = request.getRequestDispatcher("/target.jsp");dispatcher.forward(request, response);}
}
在这个例子中,OriginalServlet
接收请求后,添加了一个名为 message
的请求属性,并将其转发给 target.jsp
页面。这样,target.jsp
就可以访问这个属性并显示相应的消息。
响应重定向
- 响应重定向通过
HttpServletResponse
对象的sendRedirect()
方法实现。 - 响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,是在服务端提示下的,客户端的行为。
- 客户端至少发送了两次请求,客户端地址栏是要变化的。
- 服务端产生了多对请求和响应对象,且请求和响应对象不会传递给下一个资源。
- 因为全程产生了多个
HttpServletRequset
对象,所以请求参数不可以传递,请求域中的数据也不可以传递。 - 重定向可以是其他 Servlet 动态资源,也可以是一些静态资源以实现页面跳转。
- 重定向不可以到给 WEB-INF 下受保护的资源。
- 重定向可以到本项目以外的外部资源。
代码示例
java">import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/redirectDemo")
public class RedirectDemoServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 执行某些业务逻辑// 发送重定向响应response.sendRedirect("http://example.com");// 或者相对路径// response.sendRedirect("/newPage.jsp");}
}
在这个例子中,当用户访问 /redirectDemo
路径时,Servlet会执行一些业务逻辑,然后通过 sendRedirect()
方法告诉客户端去访问 http://example.com
或者应用内部的 /newPage.jsp
页面。
MVC 架构模式
MVC(Model-View-Controller,模型-视图-控制器)是软件工程中的一种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
模型(Model)
- 职责:负责处理应用的数据逻辑,即与数据库或数据源交互的部分。模型直接管理应用程序的数据域,并通知视图任何状态的变化,以及接收来自控制器的输入。
- 特点:
- 数据库操作(如 CRUD 操作)。
- 业务逻辑和规则的实现。
- 状态管理和数据验证。
- 功能:
- 存放和数据库对象的实体类以及一些用于存储非数据库表完整相关的 VO 对象。
- 存放一些对数据进行逻辑运算操作的的一些业务处理代码。
视图(View)
- 职责:负责展示数据,即用户界面部分。视图从模型中获取数据并决定如何显示这些数据给用户。同时,它也会接收用户的输入并将这些输入转发给控制器。
- 特点:
- 用户界面的设计与实现。
- 显示数据并允许用户与其交互。
- 可以有多个视图对应同一个模型,提供不同的展示方式。
- 功能:
- 存放一些视图文件相关的代码 html、css、js 等。
- 在前后端分离的项目中,后端已经没有视图文件,该层次已经衍化成独立的前端项目。
控制器(Controller)
- 职责:作为模型和视图之间的协调者,控制器接收用户的输入,调用模型的方法更新数据或状态,并选择合适的视图来呈现修改后的模型状态。
- 特点:
- 处理用户输入并将其转换为命令发送给模型或视图。
- 决定哪个视图应该被用来响应用户请求。
- 可能包含复杂的业务逻辑来决定如何处理特定的输入。
- 功能:
- 接收客户端请求,获得请求数据。
- 将准备好的数据响应给客户端。
MVC 模式下,项目中的常见包
-
M:
- 实体类包(pojo、entity、bean)专门存放和数据库对应的实体类和一些 VO 对象。
- 数据库访问包(dao、mapper)专门存放对数据库不同表格 CURD 方法封装的一些类。
- 服务包(service)专门存放对数据进行业务逻辑运算的一些类。
-
C:
- 控制层包(controller)。
-
V:
- web目录下的视图资源 html、css、js、img 等。
- 前端工程化后,在后端项目中已经不存在了。
非前后端分离的 MVC
前后端分离的 MVC