【Servlet 基础】
引入:
要想用户通过浏览器实现访问动态资源,得到不同的结果需要在服务器的动态资源部分,编写具有逻辑性的 Java 代码,Java 代码封装在 Java 类中,浏览器实际访问找到的是这种封装的 Java 类,这种 Java 类不是自己运行的,依赖于服务器才能运行,没有主方法(main 方法),相当于是 Tomcat 执行它
这时这个 Java 类就需要遵守一定的规则(在 Java 中就可以理解为接口,这个接口就是 Servlet),才能被 Tomcat 所识别并执行
一、 Servlet 概述
概念:全称为:server applet,是运行在服务器端的小程序
Servlet 就是一个接口,定义了 Java 类被浏览器访问到(Tomcat 识别)的规则
将来我们可以自定义一个类,实现 Servlet 接口,复写方法,进行使用
1. 版本对应
- Servlet 、JDK 、Tomcat 各版本对应的关系
查看 Tomcat 官网
- Servlet 、JSP 、EL 表达式显示的是最佳版本
(使用版本一定要保持一致,否则代码无法运行)
2. 快速入门
- 创建 JavaEE 项目 参照此文章创建 Web 项目内容
- 定义一个类,实现 Servlet 接口
- 实现接口中的抽象方法
- 配置 Servlet
- 定义一个类,实现 Servlet 接口,实现接口中的抽象方法,在此只实现一个提供服务的接口
import javax.servlet.*;
import java.io.IOException;
public class ServletTest01 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
// 提供服务的接口
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello , Servlet !");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
- 配置 Servlet,在
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>test01</servlet-name>
<servlet-class>com.example.tomcat_test.ServletTest01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test01</servlet-name>
<url-pattern>/test01</url-pattern> <!-- 在原有设定的路径下添加访问路径,注意斜杠 -->
</servlet-mapping>
</web-app>
- 在浏览器中输入:
http://localhost:8080/tomcat_test/test01
(依照自己设定的访问路径进行访问),访问的页面结果为空白,但是 IDEA 控制台输出结果(页面刷新一次,提供服务的方法会被调用一次) - 查看当前默认页(
index.jsp
)的访问路径: 运行 → 编辑配置 → 部署
(可以进行自定义设置,最好与项目名称保持一致),如下图中的:应用程序上下文 处
二、 Servlet 详解
1. Servlet 执行原理
实体分为三个部分:
- 编写的实现 Servlet 接口的类
web.xml
配置文件- 浏览器
执行部分:
- 浏览器部分:
- 通过:
http://localhost:8080/
,找到某个主机的某个应用程序(Tomcat) - 通过:
tomcat_test/
找到 Tomcat 中部署的项目 - 再通过:
/test01
(资源名称)进行访问
- 通过:
- 配置文件部分:
- 找到与配置文件
web.xml
中的<url-pattern>
吻合部分 - 再次通过相同的
<servlet-name>
标签,找到之下配置的<servlet-class>
,其中写入的全类名,利用反射路径访问
- 找到与配置文件
- Tomcat 部分:
- 之后 Tomcat 将全类名对应的字节码文件加载进内存(
Class.forName();
) - 创建对象(
clazz.newInstance();
) - 调用方法
- 之后 Tomcat 将全类名对应的字节码文件加载进内存(
总结:
- 当服务器接收到客户端浏览器的请求后,会解析请求的 URL 路径,获取访问的 Servlet 的资源路径
- 查找
web.xml
配置文件,是否有对应的<url-pattern>
标签体内容 - 如果有,则再找到对应的
<servlet-name>
全类名 - Tomcat 会将字节码文件加载进内存,并且创建其对象
- 调用具体方法
2. Servlet 方法(生命周期)
编写的类实现 Servlet 接口后,必须重写五个方法,这些方法中就包含着 Servlet 的生命周期
- 被创建:执行 init 方法,只会执行一次
- 提供服务:执行 service 方法,可以执行多次
- 被销毁:执行 destroy 方法,只会执行一次
- 编写测试类
import javax.servlet.*;
import java.io.IOException;
public class ServletTest02 implements Servlet {
/**
* 初始化的方法
* 在 Servlet 被创建时执行,只会执行一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init...初始化的方法!");
}
/**
* 获取 ServletConfig 对象
* ServletConfig :Servlet 的配置对象
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 提供服务的方法
* 每一次 Servlet 被访问时执行,可以执行多次
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service...提供服务的方法!");
}
/**
* 获取 Servlet 的一些信息,版本、作者等等信息
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 销毁的方法
* 在服务器正常关闭时执行,只会执行一次
*/
@Override
public void destroy() {
System.out.println("destroy...销毁的方法!");
}
}
- 配置
web.xml
文件,使用浏览器访问
<!-- 配置 Servlet -->
<servlet>
<servlet-name>test02</servlet-name>
<servlet-class>com.example.tomcat_test.ServletTest02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test02</servlet-name>
<url-pattern>/test02</url-pattern> <!-- 在原有设定的路径下添加访问路径,注意斜杠 -->
</servlet-mapping>
3. 生命周期详解
- 被创建:执行 init 方法,只会执行一次
- 默认情况下, Servlet 会在第一次被访问时,被创建
- 可以配置执行 Servlet 的创建时机:在
web.xml
配置文件中的<servlet>
标签下进行配置- 第一次被访问时创建
<load-on-startup>
标签中的值为负数(默认值为-1) - 在服务器启动时创建
<load-on-startup>
标签中的值为0或正整数(随便的正整数,一般指定为 0~10)
- 第一次被访问时创建
- Servlet 的 init 方法,只执行一次,说明一个 Servlet 在内存中只存在一个对象,说明 Servlet 是单例的
- 多个用户同时进行访问时,可能存在线程安全问题
- 解决:尽量不要在 Servlet 中定义成员变量,即使定义了成员变量,也不要对其修改值(保持常量状态)
- 提供服务:执行 service 方法,可以执行多次
- 每次访问 Servlet 是,service 方法都会被调用一次
- 被销毁:执行 destroy 方法,只会执行一次
- Servlet 被销毁时执行,服务器关闭时,Servlet 被销毁
- 只有服务器正常关闭时,才会执行 destroy 方法
- destroy 方法在 Servlet 被销毁之前执行,一般用于释放资源
<servlet>
<servlet-name>test02</servlet-name>
<servlet-class>com.example.tomcat_test.ServletTest02</servlet-class>
<!-- 指定 Servlet 的创建时机
1. 第一次被访问时创建
<load-on-startup> 标签中的值为负数(默认值为-1)
2. 在服务器启动时创建
<load-on-startup> 标签中的值为0或正整数(随便的正整数,一般指定为 0~10)
-->
<load-on-startup>-1</load-on-startup>
</servlet>
4. Servlet 3.0 注解配置
Servlet 3.0 支持注解配置,可以不需要配置文件
web.xml
了
步骤:
- 创建 JavaEE 项目,选择 Servlet 的版本 3.0 以上,可以不创建
web.xml
- 定义一个类,实现 Servlet 接口
- 复写方法
- 在类上使用
@WebServlet
注解,进行配置
例如:@WebServlet("资源路径")
注解 @WebServlet
源代码:
package javax.servlet.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@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 "";
}
使用:
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet(urlPatterns = "/test03") // 也可以不用写 urlPatterns,直接输入英文双引号和资源路径
public class ServletTest03 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Servlet 3.0 注解配置!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
5. IDEA 与 Tomcat 相关配置问题
-
IDEA 会为每一个 Tomcat 部署的项目单独建立一份配置文件,查看控制台的 log 日志:
Using CATALINA_BASE:
部分的路径下即为配置文件 -
工作空间项目 和 Tomcat 部署的 web 项目 区分:
- Tomcat 真正访问的是“Tomcat 部署的 web 项目”,“Tomcat 部署的 web 项目”对应着“工作空间项目”的 web 目录下的所有资源
WEB-INF
目录下的资源不能被浏览器直接访问- 断点调试:使用“小虫子”图标(debug)启动访问
6. Servlet 的体系结构
- GenericServlet 抽象类:将 Servlet 接口中的其他方法做了默认空实现,只将
service()
方法作为抽象方法,所以将来定义 Servlet 类时,可以继承 GenericServlet 类,实现service()
方法即可,如果需要使用别的方法,直接复写即可
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/test05")
public class ServletTest05 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("继承 GenericServlet 抽象类!");
}
}
引出 HttpServlet 抽象类:
由于在实现 Servlet 类中重写 service方法必须要进行:判断请求方式
伪代码:
String method=req.getMethod();
if("GET".equals(method)){
// get 方式获取数据
}else if("POST".equals(method)){
// post 方式获取数据
}
而 HttpServlet 抽象类将这个方法封装为 doGet(){}
和 doPost(){}
两个抽象方法,所以以后如果需要这种代码逻辑,直接继承 HttpServlet 抽象类,重写这两个抽象方法即可
- HttpServlet 抽象类:对 http 协议的一种封装,简化操作
- 定义类继承 HttpServlet 抽象类
- 复写
doGet()
或doPost()
方法
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("/test06")
public class ServletTest06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet 方法!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost 方法!");
}
}
7. Servlet 相关配置问题
- urlpartten :Servlet 访问路径
- 一个 Servlet 实现类可以定义多个访问路径(资源路径):
@WebServlet({"/test01","/test02","/test03"})
- 路径定义规则:
/xxx
:最常用/xxx/xxx
:多层路径,目录结构*.do
:后缀名可以随意定义,但是前面不能加/
否则会报错
- 关于
*
为通配符,可以随意书写
- 一个 Servlet 实现类可以定义多个访问路径(资源路径):