1. Servlet
1.1 什么是Servlet
Servlet 是一个小的 Java 程序,但这个程序需要运行在 Web 容器,它用于接收并响应客户端发来的请求,这些请求通常是 HTTP 请求。
我们要实现 Servlet 功能,有三种方式:第一种是实现 javax.serlvet.Servlet 接口;第二种是继承 javax.servlet.GenericServlet 类;第三种是继承 javax.servlet.http.HttpServlet 类。
一个 Servlet 程序有三个生命周期方法:初始化容器方法、处理请求的方法、销毁容器的方法。它们的调用顺序如下:
- 当客户端发来请求时,会执行初始化方法,这个方法只会执行一次
- 处理请求并响应
- 当容器销毁时,会调用Servlet 的销毁方法
1.2 第一个Servlet
1.2.1 创建Web项目
在 IDEA 中创建一个模块或工程,然后填写项目名和类型后,完成工程创建。
1.2.2 添加依赖
在项目的 pom.xml 文件中添加 Servlet 的依赖。
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
1.2.3 修改项目为web
在 pom.xml 文件中添加如下代码
<packaging>war</packaging>
然后刷新 Maven 工程,并在 src 下的 main 目录下创建 webapp 目录,用于存放 web 相关的资源。
1.2.4 配置Web容器
我们以 tomcat 为例来进行配置。
1.从tomcat的官网下载tomcat,然后解压。
2.在idea中添加应用服务
File | Settings | Build, Execution, Deployment | Application Servers
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ZlKJUC2-1654505083467)(image/image-20220224101721067.png)]
点击 + 号选择 Tomcat Server ,然后找到 tomcat 的安装目录。
3.部署项目到 tomcat 中运行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AbOpAKWT-1654505083468)(image/image-20220224101837263.png)]
点击上图中的向下箭头,找到 Edit Configuration…, 点击后进入配置界面,找到 Tomcat Servlet 下的 Local,进行项目配置界面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dq2HlTCU-1654505083468)(image/image-20220224101948978.png)]
在右边的 Deployment 标签中去点击 + 号选择 Artifact…,然后选择带有 expload…的war包进行部署。
1.2.5 编写Servlet程序
package com.xianopeng.servelt;
import javax.servlet.*;
import java.io.IOException;
/**
* @Description 第一个Servlet 程序,去实现 Servlet 接口
* @Author Jock
* @Date 2022/2/24
*/
public class FirstServlet implements Servlet {
// 初始化方法
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("FirstServlet 已经初始化了........");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("接收到了请求");
res.getWriter().write("hello");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("容器已经销毁");
}
}
1.2.6 配置Servlet程序
在项目中创建 web.xml 文件,这个文件的路径应该是在 src/main/webapp/WEB-INF/web.xml
<?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_4_0.xsd"
version="4.0">
<!-- 配置 Servlet -->
<servlet>
<servlet-name>firstServlet</servlet-name>
<servlet-class>com.xianopeng.servelt.FirstServlet</servlet-class>
<load-on-startup>3</load-on-startup>
<init-param>
<param-name>name</param-name>
<param-value>zs</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>firstServlet</servlet-name>
<url-pattern>/first</url-pattern>
</servlet-mapping>
</web-app>
在 web.xml 文件中,使用 标签来配置一个 Servlet 程序,它里有两个很重要子标签:
- servlet-name:用于定义 servlet 的名称,它也就是引用名称,或者是实例化的名称,在同一个 web.xml 文件,这个名称必须唯一。一般是类名首字母小写
- servlet-class:就是 Servlet 类的完整名称(包名 + 类名)
- load-on-startup:用于指定这个 Servlet 的加载时机,一般在 1 之后,如果希望更早的加载,那么可以定义为1。值越小越先加载。
- init-param:用于对这个 Servlet 进行参数配置,可以有多个
- param-name:参数名
- param-value:参数值
- servlet-mapping:用于对Servlet 进行映射
- servlet-name:必须和 servlet 标签中的 servlet-name 标签中配置的名称一致
- url-pattern:用于配置请求的路径,必须是以 / 线开头
1.2.7 继承 GenericServlet
除了实现 Servlet 接口外,还可以继承 GenericServlet 抽象类来编写Servlet程序
package com.xianopeng.servelt;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* @Description 第二个Servlet程序
* @Author Jock
* @Date 2022/2/24
*
*/
public class SecondServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("ok");
res.getWriter().write("ok");
}
}
GenericServlet这个类已经实现 Servlet 接口中大部分的方法,但没有实现 service() 这个核心方法,所以它是一个抽象类
配置:
<servlet>
<servlet-name>secondServlet</servlet-name>
<servlet-class>com.xianopeng.servelt.SecondServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>secondServlet</servlet-name>
<url-pattern>/second</url-pattern>
</servlet-mapping>
1.2.8 继承HttpServlet
我们的请求绝大部分都是 HTTP 请求,因此我们在真正开发中使用的是继承 HttpServlet 这个类。这也是我们需要掌握的实现方式。
package com.xianopeng.servelt;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Description 第三个Servlet,继承HttpServlet
* @Author Jock
* @Date 2022/2/24
*/
public class ThirdServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ok");
resp.getWriter().write("ok ok");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
配置 Servlet
<servlet>
<servlet-name>thirdServlet</servlet-name>
<servlet-class>com.xianopeng.servelt.ThirdServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>thirdServlet</servlet-name>
<url-pattern>/third</url-pattern>
</servlet-mapping>
1.3 使用注解
从 Servlet3.0开始,已经支持注解方式的开发。
package com.xianopeng.servelt;
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 使用注解
* @Author Jock
* @Date 2022/2/24
*/
@WebServlet(name = "fourServlet", value = "/four")
public class FourServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("fourServlet");
}
}
WebServlet注解的源码:
package javax.servlet.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.Documented;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
String name() default "";
String[] value() default {};
String[] urlPatterns() default {};
int loadOnStartup() default -1;
WebInitParam [] initParams() default {};
boolean asyncSupported() default false;
String smallIcon() default "";
String largeIcon() default "";
String description() default "";
String displayName() default "";
}
在这个类中 name 属性对应 web.xml 文件中的 servlet-name 标签,value 和 urlPatterns 对应 servlet-mapping 中的 url-pattern 标签。
2. ServletConfig
这个类的源码为:
package javax.servlet;
import java.util.Enumeration;
public interface ServletConfig {
public String getServletName();
public ServletContext getServletContext();
public String getInitParameter(String name);
public Enumeration<String> getInitParameterNames();
}
这个类定义了获取 Servlet 的名称、Servlet 初始化参数和 ServletContext 对象。
使用示例:
package com.xianopeng.servelt;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
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(name = "fiveServlet", urlPatterns = "/five",
initParams = {
@WebInitParam(name = "driver", value = "com.mysql.jdbc.Driver"),
@WebInitParam(name = "username", value = "root"),
@WebInitParam(name = "password", value = "123456")
})
public class FiveServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取Servlet的名称
String servletName = this.getServletConfig().getServletName();
System.out.println("servletName = " + servletName);
// 获取初始化参数
String driver = this.getServletConfig().getInitParameter("driver");
System.out.println("driver = " + driver);
String username = this.getServletConfig().getInitParameter("username");
System.out.println("username = " + username);
// 获取所有的初始化参数名称
Enumeration<String> names = this.getServletConfig().getInitParameterNames();
while (names.hasMoreElements()) {
String key = names.nextElement();
String val = this.getServletConfig().getInitParameter(key);
System.out.println(key + " = " + val);
}
// 获取servletContext对象
ServletContext servletContext = this.getServletConfig().getServletContext();
System.out.println("servletContext = " + servletContext);
}
}
输出结果为:
servletName = fiveServlet driver = com.mysql.jdbc.Driver username = root password = 123456 driver = com.mysql.jdbc.Driver username = root servletContext = org.apache.catalina.core.ApplicationContextFacade@54f59b62
3. ServletContext
这个类也是一个接口,它定义Servlet 运行上下文环境,这个类中的方法如下:
我们用得比较多的就是 getContextPath() 方法。
package com.xianopeng.servelt;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
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;
/**
* @Description TODO
* @Author Jock
* @Date 2022/2/24
*/
@WebServlet(name = "fiveServlet", urlPatterns = "/five")
public class FiveServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取servletContext对象
ServletContext servletContext = this.getServletConfig().getServletContext();
System.out.println("servletContext = " + servletContext);
// 获取我们的项目名称
String contextPath = servletContext.getContextPath();
System.out.println("contextPath = " + contextPath);
// 获取项目的绝对路径
String realPath = servletContext.getRealPath("/");
System.out.println("realPath = " + realPath);
// 设置全局变量
servletContext.setAttribute("aaa", "bbbb");
// 获取全局变量
Object aaa = servletContext.getAttribute("aaa");
System.out.println("aaa = " + aaa);
}
}
运行后输出的结果为:
servletContext = org.apache.catalina.core.ApplicationContextFacade@4c69231a contextPath = /servlet_first realPath = D:\Workspace\Idea\JavaWeb\servlet-first\target\servlet-first-1.0-SNAPSHOT\ aaa = bbbb
4. HttpServletRequest
HttpServletRequest 是一个接口,它封装了 HTTP 请求相关的信息,由 Servlet 容器来创建它的实例对象并传入到 service() 方法中。接口中定义的方法如下:

4.1 获取请求参数
为了更好的演示我们的功能,我们再次创建一个 Maven 的 web 模块。
然后添加 servlet 依赖和 tomcat 插件。
pom.xml 文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xianopeng</groupId>
<artifactId>servlet-demo</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</build>
</project>
4.2 编写HTML页面
我们在 webapp 目录下创建 index.html 页面,在这个页面中我们添加一个表单,用户向 Servlet 请求数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="account" placeholder="账号"><br>
<input type="password" name="password" placeholder="密码"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
4.3 编写登录Servlet
package com.xianopeng.servlet;
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("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置编码格式
req.setCharacterEncoding("utf-8");
// 获取请求参数
String account = req.getParameter("account");
System.out.println("account = " + account);
String password = req.getParameter("password");
System.out.println("password = " + password);
}
}
运行项目后,在 index.html 表单输入相应的信息后,就可以在 Servlet 程序中获取到。
4.4 常用方法介绍
package com.xianopeng.servlet;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置编码格式
req.setCharacterEncoding("utf-8");
// 获取请求参数
String account = req.getParameter("account");
System.out.println("account = " + account);
String password = req.getParameter("password");
System.out.println("password = " + password);
// HttpServletRequest 常用方法介绍
// 1. 获取请求的方式
String method = req.getMethod();
System.out.println("method = " + method);
// 2. 获取指定的请求头
String header = req.getHeader("Content-Type");
System.out.println("header = " + header);
// 3. 获取Session 对象
HttpSession session = req.getSession();
System.out.println("session = " + session);
// 4. 设置属性
req.setAttribute("name", account);
// 5. 获取属性
Object name = req.getAttribute("name");
System.out.println("name = " + name);
}
}
运行后结果为:
account = 西安
password = 123456
method = POST
header = application/x-www-form-urlencoded
session = org.apache.catalina.session.StandardSessionFacade@54f818f1
name = 西安
4.5 转发
在 HttpServletRequest 对象有一个实现转发的方法。为了演示转发功能,我们编写两个 Servlet 程序。
1)FromServlet.java
package com.xianopeng.servlet;
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("/from")
public class FromServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 向 HttpServletRequest 对象中存入一个值
req.setAttribute("name", "zhang san");
// 转发到 ToServlet 中
req.getRequestDispatcher("/to").forward(req, resp);
}
}
2)ToServlet.java
package com.xianopeng.servlet;
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("/to")
public class ToServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 HttpServletRequest 对象中的 name 属性值
String name = (String) req.getAttribute("name");
System.out.println("name = " + name);
}
}
我们运行服务,然后在浏览器地址栏中输入 /from 来查看效果。
从运行的结果可以发现,在控制台中输出了 zhang san,说明请求已经从 from 转发到了 to。
4.6 重定向
要实现重定向功能,需要使用 HttpServletResponse 对象中的 sendRedirect(String local) 方法。
修改 FromServlet.java 类中页面跳转方式。
package com.xianopeng.servlet;
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("/from")
public class FromServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 向 HttpServletRequest 对象中存入一个值
req.setAttribute("name", "zhang san");
// 转发到 ToServlet 中
// req.getRequestDispatcher("/to").forward(req, resp);
// 重定向
resp.sendRedirect(req.getContextPath() + "/to");
}
}
而 ToServlet.java 代码无需修改。
程序运行后的结果发现在 ToServlet 中不能成功获取到 name 属性的值。
4.7 转发和重定向的区别
1.转发的方法是 HttpServletRequest 对象中的方法,而重定向是 HttpServletReponse 对象中的方法。
2.转发发送一次请求,而重定向会发出多次请求。
3.转发浏览器地址栏不会发生变化,而重定向浏览器地址栏会发生变化。
4.在一个转发链中可以使用 HttpServletRequest 对象中的属性,而重定向不能使用 HttpServletRequest 对象中的属性。