深入理解 Java Servlet:从基础到实战
目录
一、引言
二、Servlet 概述
2.1 JavaWeb 的三大组件
2.2 Servlet 的作用
三、Servlet 初识
3.1 第一个 Servlet
3.1.1 Servlet 说明
3.1.2 Servlet 接口
3.1.3 创建 Servlet
3.1.4 JavaWeb 请求响应流程
3.2 Servlet 生命周期
四、HttpServlet
4.1 HttpServlet 介绍
4.2 Http 请求方法
4.3 创建 HttpServlet
4.3.1 第一种方法
4.3.2 第二种方法
4.3.3 Servlet 创建顺序
五、ServletConfig
六、Servlet 路径映射
6.1 Url-Pattern 的配置类型
6.2 优先级
6.3 示例分析
七、相对路径和绝对路径
7.1 相对路径
7.2 绝对路径
八、ServletContext
8.1 ServletContext 介绍
8.2 ServletContext API
8.3 ServletContext 使用
8.3.1 常规应用
一、引言
在 Java Web 开发领域,Servlet 是至关重要的基础组件。它如同一个勤劳的小助手,负责处理来自客户端的各种请求。本文将全面深入地介绍 Servlet 的相关知识,包括其概念、生命周期、创建方式、路径映射等内容,同时会结合实际代码示例,帮助大家更好地掌握 Servlet 的使用。
二、Servlet 概述
2.1 JavaWeb 的三大组件
Servlet 是 JavaWeb 三大组件之一,另外两个组件是 Filter(过滤器)和 Listener(观察者模式)。Servlet 是我们学习 JavaWeb 的基石,必须熟练掌握。
2.2 Servlet 的作用
Servlet 即 Server Let,其主要作用是处理用户请求。当客户端发出请求后,Tomcat 会找到合适的 Servlet 来处理该请求。例如,用户发出登录请求,就会由处理登录的 Servlet 进行处理。
三、Servlet 初识
3.1 第一个 Servlet
3.1.1 Servlet 说明
Servlet 是运行在 Web 服务器中的小型 Java 程序,通常通过 HTTP 接收和响应来自 Web 客户端的请求。我们自己编写的 Servlet 必须实现 javax.servlet.Servlet
接口,并且要在 web.xml
文件中进行部署,否则 Tomcat 无法找到该 Servlet。
3.1.2 Servlet 接口
javax.servlet.Servlet
接口包含以下几个重要方法:
void init(ServletConfig servletConfig)
:Tomcat 创建 Servlet 实例后,马上调用该方法进行初始化,且只调用一次。void service(ServletRequest request, ServletResponse response)
:每次处理请求时都会调用该方法。void destroy()
:Tomcat 销毁 Servlet 实例前会先调用该方法。ServletConfig getServletConfig()
:返回ServletConfig
对象,通常在init()
方法中保存参数并在此返回。String getServletInfo()
:返回一个说明当前 Servlet 的字符串,基本无用。
3.1.3 创建 Servlet
init 初始化
当Tomcat创建Servlet实例后,马上调用init()方法。这个方法只在创建后调用一次!用来做Servlet初始化工作!一个Servlet实例只被创建一次,所以init()方法也只被调用一次!
getServletConfig
- 这个方法返回ServletConfig对象,但我们不能自己去创建ServletConfig对象,所以一般我们会在init()方法中把init()方法的参数保存起来,然后再在本方法中返回它。ServletConfig对象对应web.xml中当前Servlet实例的配置信息。
service
- void service(ServletRequest request, ServletResponse response):Servlet实例在每次处理请求时都调用service()方法。
destory
- void destroy():当Tomcat要销毁Servlet实例时,会先调用destroy()方法,再销毁它。所谓销毁Servlet,其实就是在Servlet缓存池中把Servlet移除!一般只有Tomcat关闭时,才会销毁Servlet实例!
getServletInfo
- String getServletInfo():这个方法只是返回一个字符串,用来说明当前Servlet。基本没用!
同时,需要在 web.xml
文件中配置 Servlet 的访问路径:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>cn.tx.servlet.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
3.1.4 JavaWeb 请求响应流程
当 Tomcat 接收到请求(如 http://localhost:8080/servlet_pro/logon
)后,会按以下步骤处理:
- 找到
servlet_pro
项目中的web.xml
文件。 - 通过请求路径查找处理该请求的 Servlet 类型。
- 根据匹配的
url-pattern
找到对应的servlet-name
。 - 再通过
servlet-name
找到servlet-class
。 - Tomcat 检查内存中是否存在该 Servlet 对象,若存在则直接调用其
service()
方法;若不存在则通过反射创建实例并调用service()
方法。
3.2 Servlet 生命周期
javax.servlet.Servlet
接口中的三个方法说明了 Servlet 的生命周期:
void init(ServletConfig)
:创建后马上调用进行初始化。void service(ServletRequest,ServletResponse)
:每次处理请求时调用。void destroy()
:销毁 Servlet 实例前调用。
默认情况下,Servlet 对象的实例在浏览器第一次调用时被创建,也可以通过配置修改创建时机。需要注意的是,只有这三个方法是生命周期方法,由 Tomcat 在不同时间点调用,其他自定义方法不会被 Tomcat 调用,但可以在生命周期方法中调用。
四、HttpServlet
4.1 HttpServlet 介绍
由于现在的请求大多基于 HTTP 协议,因此我们可以专门为 HTTP 请求编写一个通用的父类 HttpServlet
。HttpServlet
是 GenericServlet
的子类,而 GenericServlet
又是 Servlet
接口的子类。
4.2 Http 请求方法
HTTP 请求方法除了常见的 GET 和 POST 外,还有 PUT、DELETE、CONNECT 等,但大部分使用场景较少。
- GET:通过请求 URI 得到资源。
- POST:用于添加新的内容。
- PUT:用于修改某个内容。
- DELETE:删除某个内容。
- CONNECT:用于代理进行传输,如使用 SSL。
- OPTIONS:询问可以执行哪些方法。
- PATCH:部分文档更改。
- TRACE:用于远程诊断服务器。
- HEAD:类似于 GET,但不返回 body 信息,用于检查对象是否存在及获取元数据。
4.3 创建 HttpServlet
4.3.1 第一种方法
创建一个类继承 HttpServlet
,并在 web.xml
中配置映射路径:
4.3.2 第二种方法
在 IDEA 中可以直接创建 Servlet,创建完成后自行填写映射路径。这种也是注释方法。直接在参数添加路径
4.3.3 Servlet 创建顺序
有些 Servlet 需要在 Tomcat 启动时就被创建,可以在 web.xml
文件的 <servlet>
元素中添加 <load-on-startup>
元素:
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>cn.tx.servlet.Servlet1</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
当 <load-on-startup>
的值为 0 或者大于 0 时,表示容器在应用启动时就加载这个 Servlet,并且该值是一个序号,Tomcat 会按此顺序创建 Servlet 实例。
重写init方法
五、ServletConfig
ServletConfig
对象对应 web.xml
文件中的 <servlet>
元素。我们不能自己创建 ServletConfig
对象,它会作为 init()
方法的参数由 Tomcat 传递。可以通过它获取当前 Servlet 在 web.xml
文件中的配置信息,例如配置名、初始化参数等。
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>cn.tx.servlet.Servlet1</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>123456</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>abcd</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
public class Servlet1 implements Servlet {
private ServletConfig servletConfig;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
this.servletConfig = servletConfig;
String username = servletConfig.getInitParameter("username");
System.out.println("username:" + username);
Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String element = parameterNames.nextElement();
System.out.println(element + ":" + servletConfig.getInitParameter(element));
}
}
// 其他方法...
}
打印输出
六、Servlet 路径映射
6.1 Url-Pattern 的配置类型
- 完全路径匹配:以
/
开头,如/aaa
、/aaa/bbb
。 - 目录匹配:以
/
开头,如/aaa/*
、/*
。 - 扩展名匹配:不能以
/
开头,如*.do
、*.action
。
6.2 优先级
完全路径匹配 > 目录匹配 > 扩展名匹配。需要注意避免出现 /* .do
这样的错误配置。
6.3 示例分析
假设有以下映射关系:
- Servlet11 映射到
/abc/*
- Servlet22 映射到
/*
- Servlet33 映射到
/abc
- Servlet44 映射到
*.do
对于不同的 URL 请求,Servlet 引擎的响应规则如下:
URL 为 /abc/a.html
时,/abc/*
和 /*
都匹配,由于 /abc/*
限定范围更精确,所以调用 Servlet11 响应。
- URL 为
/abc
时,/abc/*
和/abc
都匹配,调用 Servlet33 响应。 - URL 为
/abc/a.do
时,/abc/*
和*.do
都匹配,调用 Servlet11 响应。 - URL 为
/a.do
时,/*
和*.do
都匹配,调用 Servlet22 响应。 - URL 为
/xxx/yyy/a.do
时,/*
和*.do
都匹配,调用 Servlet22 响应。
七、相对路径和绝对路径
7.1 相对路径
相对路径根据当前资源路径与目标资源路径的相对位置关系,通过 .
(当前目录)和 ..
(上一级目录)访问目标资源。其编写规则会根据当前路径不同而变化。
7.2 绝对路径
绝对路径是指目录下的绝对位置,直接到达目标位置。包括带有协议的完整路径(如 http://localhost/day5/hello
)和以 /
开始的路径(如 /day5/hello
)。需要注意服务器端和客户端对于 /
的区别,客户端访问路径和服务器内部路径有所不同。
八、ServletContext
8.1 ServletContext 介绍
ServletContext
是一个全局的储存信息的空间,服务器启动时创建,关闭时释放。所有用户共用一个 ServletContext
对象,因此可以在其中存放所有用户需要共享的、线程安全的重要信息。
8.2 ServletContext API
Object getAttribute(String name)
:返回指定名称的属性值,若不存在则返回null
。String getContextPath()
:返回 Web 应用的上下文路径。String getInitParameter(String name)
:返回指定名称的上下文初始化参数值,若不存在则返回null
。String getRealPath(String path)
:返回给定虚拟路径的真实路径。void setAttribute(String name, Object object)
:将对象绑定到指定名称的属性上。InputStream getResourceAsStream(String path)
:返回指定路径的资源作为输入流。
8.3 ServletContext 使用
8.3.1 常规应用
- 获取 WEB 应用的全局初始化参数:
<context-param>
<param-name>company</param-name>
<param-value>bbbb</param-value>
</context-param>
init 加入以下内容
ServletContext servletContext = config.getServletContext();
String company = servletContext.getInitParameter("company");
System.out.println("company:" + company);
- 通过 ServletContext 对象实现数据共享:统计站点访问次数
-
package com.qcby.servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Enumeration; public class Login1Servlet extends HttpServlet{ private String username; private String password; private ServletConfig servletConfig; private String company; @Override public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); ServletContext servletContext = servletConfig.getServletContext(); company = servletContext.getInitParameter("company"); System.out.println("company:" + company); this.servletConfig = servletConfig; String username = servletConfig.getInitParameter("username"); System.out.println("username:" + username); Enumeration<String> parameterNames = servletConfig.getInitParameterNames(); while (parameterNames.hasMoreElements()) { String element = parameterNames.nextElement(); System.out.println(element + ":" + servletConfig.getInitParameter(element)); } System.out.println("login1init"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = getServletContext(); Integer visitNums = (Integer) (servletContext.getAttribute("visitNums")); visitNums = visitNums == null ? 0 : visitNums; servletContext.setAttribute("visitNums", ++visitNums); req.setCharacterEncoding("UTF-8"); resp.getWriter().write("当前网站访问人次:" + visitNums); System.out.println("当前网站访问人次: " + visitNums); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login Form</title> </head> <body> <form action="login1" method="post"> <input type="submit" value="发送 POST 请求"> </form> </body> </html>