目录
[1. Servlet概述(了解)](#1. Servlet概述(了解))
[1.1. JavaWeb的三大组件](#1.1. JavaWeb的三大组件)
[1.2. Servlet的作用](#1.2. Servlet的作用)
[2. Servlet初识(熟练)](#2. Servlet初识(熟练))
[2.1. 第一个Servlet](#2.1. 第一个Servlet)
[2.1.1. Servlet说明](#2.1.1. Servlet说明)
[2.1.2. Servlet接口](#2.1.2. Servlet接口)
[2.1.3 创建Servlet](#2.1.3 创建Servlet)
[2.1.4 JavaWeb请求响应流程](#2.1.4 JavaWeb请求响应流程)
[2.2 Servlet生命周期](#2.2 Servlet生命周期)
[3.1 HttpServlet介绍](#3.1 HttpServlet介绍)
[3.2 Http请求方法](#3.2 Http请求方法)
[3.3 创建HttpServlet](#3.3 创建HttpServlet)
[3.3.1 第一种方法](#3.3.1 第一种方法)
[3.3.2 第二种方法](#3.3.2 第二种方法)
[3.3.3 Servlet创建顺序](#3.3.3 Servlet创建顺序)
[6.1 相对路径](#6.1 相对路径)
[6.2 绝对路径](#6.2 绝对路径)
[7.1 ServletContext介绍](#7.1 ServletContext介绍)
[7.2 ServletContext API](#7.2 ServletContext API)
[7.3 ServletContext使用](#7.3 ServletContext使用)
[7.3.1 常规应用](#7.3.1 常规应用)
[7.3.2 通过ServletContext对象读取资源文件](#7.3.2 通过ServletContext对象读取资源文件)
[8.1 单例的Servlet 单例多线程](#8.1 单例的Servlet 单例多线程)
[8.2 Servlet的生命周期](#8.2 Servlet的生命周期)
1. Servlet概述(了解)
1.1. JavaWeb的三大组件
Servlet是JavaWeb三大组件之一,它是我们学习JavaWeb最为基本的组件,也就是说你一定要**100%**的掌握它。
其它两种:Filter(过滤器)、Listener(观察者模式),后续讲解
1.2. Servlet的作用
Servlet,即Server Let的意思,用来处理用户请求。当客户端发出请求后,由Tomcat去找到可以处理这一请求的Servlet来处理。
也就是说,用户的请求是由Servlet来处理的!例如用户发出登录请求,那么就应该由处理登录的Servlet来处理;用户发出登录请求,那么就应该由登录Servlet来处理。

项目

Tomcat先加载web.xml
2. Servlet初识(熟练)
2.1. 第一个Servlet
2.1.1. Servlet说明
servlet 是运行在 Web 服务器中的小型 Java 程序。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。也就是说,Servlet是由我们自己来完成的!但Servlet一定要实现javax.servlet.Servlet接口,并且还要在web.xml文件中部署!不然Tomcat是找不到我们写的Servlet的。(因为Tomcat一启动,就去访问web.xml配置文件)
2.1.2. Servlet接口
javax.servlet.Servlet接口中方法如下:
Ø void init(ServletConfig servletConfig):当Tomcat创建Servlet实例后,马上调用init()方法。这个方法只在创建后调用一次!用来做Servlet初始化工作!一个Servlet实例只被创建一次,所以init()方法也只被调用一次!(本方法编写对Servlet的初始化代码)
初始化:一开始去干的事,比如登录,登陆之前要干的。或者:晚上去教室学习,第一件事一定是开灯,每个人都要干,那么开灯这个事情可以放在 init()里。一调用这个类,就去开灯。
Ø void service(ServletRequest request, ServletResponse response):Servlet实例在每次处理请求时都调用service()方法。
Ø void destroy():当Tomcat要销毁Servlet实例时,会先调用destroy()方法,再销毁它。所谓销毁Servlet,其实就是在Servlet缓存池中把Servlet移除!一般只有Tomcat关闭时,才会销毁Servlet实例!
Ø ServletConfig getServletConfig():这个方法返回ServletConfig对象,但我们不能自己去创建ServletConfig对象,所以一般我们会在init()方法中把init()方法的参数保存起来,然后再在本方法中返回它。ServletConfig对象对应web.xml中当前Servlet实例的配置信息。
Ø String getServletInfo():这个方法只是返回一个字符串,用来说明当前Servlet。基本没用!
2.1.3 创建Servlet
第一步:常见HelloServlet实现Servlet的接口,实现接口中的方法
java
public class Servlet1 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 {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
点击Servlet,Ctrl+h

第二步:配置servlet的访问路径
XML
<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"
metadata-complete="true">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.qcby.servlet.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
2.1.4 JavaWeb请求响应流程
Tomcat一启动,就去加载web.xml配置文件,根据请求里的路径,找到url-pattern对应的名字,根据名字找到对应的类。


当Tomcat接收到请求(http://localhost:8080/javaWeb/hello)后,Tomcat会找到 javaWeb 项目中的 web.xml 文件,然后通过 hello 这个请求路径,查找处理这个请求的Servlet类型。这刚好与 /hello匹配,这说明存在一个可以通过这个请求的Servlet。然后再通过/hello 查找到hello,然后再通过hello 查找到com.qcby.servlet.Servlet1。这时Tomcat已经得到了一个Servlet类名字(一个字符串而已)。
Tomcat通过Servlet类名字去查找内存中是否存在Servlet对象,如果存在,那么就不用再去创建,直接获取这个Servlet实例,调用它的service()方法完成请求!
如果这个Servlet不存在,那么Tomcat会通过反射来创建Servlet实例,并把Servlet实例存放到Servlet池中,再去调用Servlet的service方法处理请求。
2.2 Servlet生命周期
javax.servlet.Servlet接口中,有三个方法说明了Servlet的生命周期:
Ø void init(ServletConfig): 创建后马上调用init()完成初始化;
Ø void service(ServletRequest,ServletResponse): 每次处理请求时调用service()方法;
Ø void destroy(): 当Tomcat要销毁Servlet实例时,先调用destroy()方法。

Servlet对象的实例默认情况下是在浏览器第一次调用servlet时候被创建的(可以修改其创建时机后续讲解)
现在你应该已经清楚了,Servlet的实例不由我们创建,Servlet的方法不由我们来调用,这一切都是由Tomcat来完成!!!这就是说由Tomcat来管理Servlet,而我们只需要去编写Servlet实现类,并将其部署到web.xml文件中去!
再次提醒,只有这三个方法是生命周期中的方法。也就是说,生命周期方法会被Tomcat在不同的时间点来调用!而其它方法就不会被调用了!!!如果你在自己写的Servlet中添加了其他方法,那么Tomcat也是不会去调用它们的!但你可以让生命周期方法去调用你自己写的方法就OK了!
3.HttpServlet
3.1 HttpServlet介绍
因为现在我们的请求都是基于HTTP协议的,所以我们应该专门为HTTP请求写一个Servlet做为通用父类。

由上图我们可以看出,以后再写Servlet 可以直接继承HttpServlet
Ø Servlet 一个标准
Ø GenericServlet 是Servlet接口子类
Ø HttpServlet 是GenericServlet子类,一个专门处理Http请求的Servlet
3.2 Http请求方法
HTTP请求方法不只是GET和POST,还有其他的方法,但基本上用不上。这里只是简单介绍一下。你自己心里有个数,HTTP请求除了GET和POST之外还有别的就行了。
& GET 通过请求URI得到资源
& POST 用于添加新的内容
& PUT 用于修改某个内容
& DELETE 删除某个内容
& CONNECT 用于代理进行传输,如使用SSL
& OPTIONS 询问可以执行哪些方法
& PATCH 部分文档更改
& RACE 用于远程诊断服务器
& HEAD 类似于GET, 但是不返回body信息,用于检查对象是否存在,以及得到对象的元数据。
& TRACE 用于远程诊断服务器
3.3 创建HttpServlet
3.3.1 第一种方法
xml方式:适用于配置多个Servlet
创建一个类继承HttpServlet:处理Http请求的Servlet
java
package com.qcby.servlet;
import javax.servlet.http.HttpServlet;
public class Servlet2 extends HttpServlet {
}

配置Servlet的映射路径
XML
<servlet>
<servlet-name>hello2</servlet-name>
<servlet-class>com.qcby.servlet.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
3.3.2 第二种方法
IDEA直接创建servlet


ctrl+o重写。
创建完成,自己填写映射路径
@WebServlet表示这个类是一个Servlet

这个Servlet的名称是Servlet4,映射路径是/hello4
3.3.3 Servlet创建顺序
有些Servlet需要在Tomcat启动时就被创建,而不是第一次访问时被创建,那么可以在web.xml文件中配置 <servlet> 元素。
在 <servlet> 元素中添加子元素 <load-on-startup> 元素!
作用:
在 Web 应用启动时,Servlet 容器(如 Tomcat)默认情况下会在第一次收到针对某个 Servlet 的请求时才加载并初始化该 Servlet。但通过 <load-on-startup>
元素,可以指定某些 Servlet 在 Web 应用启动时就被加载和初始化,而不是等到第一次请求时才进行。
好处:
- 提前初始化资源 :如果 Servlet 在初始化时需要进行一些耗时的操作,如加载配置文件、建立数据库连接等,使用
<load-on-startup>
可以在应用启动时就完成这些操作,避免在第一次请求时才执行这些操作导致的响应延迟。 - 确保资源可用性:某些 Servlet 可能会在初始化时创建一些全局资源,如缓存、线程池等,提前加载这些 Servlet 可以确保这些资源在应用启动后就可用。
XML
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.qcby.servlet.Servlet1</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>


当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;
所有添加了子元素的Servlet,都会在Tomcat启动时被创建!当然,只是被创建,但没有处理请求!但我们知道在Servlet生命周期中init()方法会在创建后被调用,所以你可以在init()方法中做一些输出,查看是否在Tomcat启动时调用了它。
元素的值是一个序号,Tomcat会使用这个序号给多个Servlet排序!然后在Tomcat启动时会按这个顺序来创建Servlet实例对象!

4.ServletConfig
ServletConfig对象对应web.xml文件中的元素。例如你想获取当前Servlet在web.xml文件中的配置名,那么可以使用**servletConfig.getServletName()**方法获取!
你不能自己去创建ServletConfig对象,Servlet的init()方法的参数就是ServletConfig类型的。Tomcat在调用init()方法时,会传递ServletConfig对象。你可以在init()方法中使用它!
XML
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.qcby.servlet.Servlet1</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>qbjava</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
参数列表里配置ServletConfig,通过ServletConfig调用方法可以得到xml里设置的参数。
java
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));
}
添加了两个初始化参数,第一个参数的名称为paramName1,第一个参数的值为paramValue1;第二个参数的名称为paramName2,第二个参数值为paramValue2。
在 <servlet> 元素中可以加载多个 <init-param> ,每个 <init-param> 表示一个参数。 <init-param> 下有两个子元素 :<param-name> 和 <param-value> , 其中 <param-name> 表示参数的名称,而 <param-value> 元素参数的值。
注意,
<param-name> 是添加到<servlet> 元素中,而不是 <servlet-mapping> 中。
使用ServletConfig对象的getInitParameter(String paramName)方法可以获取指定参数名称的参数值。getInitParameterNames()方法返回所有参数的名字,返回值类型为Enumeration。
5.Servlet路径映射
关于Url-Pattern的配置:
Ø 完全路径匹配 以/开头 例如 /aaa /aaa/bbb
Ø 目录 匹配 以/开头 例如 /aaa/* /*
Ø 扩展名 匹配 不能以/开头 例如 *.do *.action ....
优先级: 完全路径匹配 > 目录匹配 > 扩展名匹配
经典错误: /*.do
举例:
对于如下的一些映射关系:
Ø Servlet11 映射到 /abc/*
Ø Servlet22 映射到 /*
Ø Servlet33 映射到 /abc
Ø Servlet44 映射到 *.do
问题:
Ø URL为"/abc/a.html","/abc/*"和"/*"都匹配,哪个servlet响应
Servlet引擎将调用Servlet1。同样是目录匹配所限定范围更精确的先匹配
Ø URL为"/abc"时,"/abc/*"和"/abc"都匹配,哪个servlet响应
Servlet引擎将调用Servlet3。
Ø URL为"/abc/a.do"时,"/abc/*"和"*.do"都匹配,哪个servlet响应
Servlet引擎将调用Servlet1。
Ø URL为"/a.do"时,"/*"和"*.do"都匹配,哪个servlet响应
Servlet引擎将调用Servlet2.
Ø URL为"/xxx/yyy/a.do"时,"/*"和"*.do"都匹配,哪个servlet响应
Servlet引擎将调用Servlet2。
服务器端路径 /demo4 (转发、包含...)
6.相对路径和绝对路径
Java中使用的路径,分为两种:绝对路径和相对路径。归根结底,Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法,都不过是一些便利方法。不过是API在底层帮助我们构建了绝对路径,从而找到资源的!
我们以下面的项目路径来进行举例:

6.1 相对路径
相对路径,根据当前资源路径 与 目标资源路径,寻找相对位置关系,通过 . (当前目录) 和 .. (上一级目录) 访问目标资源;
综上所述,我们发现相对路径总需要分析当前路径与目标路径对应关系,编写规则会根据当前路径不同而不同。
6.2 绝对路径
绝对路径是指目录下的绝对位置,直接到达目标位置
带有协议完整路径 (跨网站) http://localhost/day5/hello
以/ 开始路径 (同一个站点内) : /day5/hello
服务器端和客户端对于/ 的区别
客户端访问路径:/day5/hello
服务器内部路径:/hello, 多用于服务器跳转, 页面包含(jsp)
7.ServletContext
7.1 ServletContext介绍
ServletContext是一个全局的储存信息的空间,服务器开始,其就存在,服务器关闭,其才释放。
ServletConfig和ServletContext区别:
ServletConfig: 每个Servlet在初始化时,Servlet容器会为其创建一个ServletConfig对象,并在调用Servlet的init(ServletConfig config) 方法时将其传递给Servlet。ServletConfig对象包含了Servlet的初始化参数信息,这些参数通过标签在web.xml中配置。 ServletConfig对象仅对具体的某一个Servlet有效,并且不能被其他Servlet访问.
ServletContext: Web容器在启动时会为每个Web应用程序创建一个唯一的ServletContext对象。ServletContext对象可以看作是Web应用的共享内存,所有的Servlet都可以访问这个对象。
request,一个用户可有多个; 请求域对象
session,一个用户一个; 会话域对象
而servletContext,所有用户共用一个。应用域对象
所以,为了节省空间,提高效率,ServletContext中,要放必须的、重要的、所有用户需要共享的线程又是安全的一些信息。

Ø WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
Ø 由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
Ø 查看ServletContext API文档,了解ServletContext对象的功能。
7.2 ServletContext API
|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Object | getAttribute(String name) Returns the servlet container attribute with the given name, or null if there is no attribute by that name. |
| String | getContextPath() Returns the context path of the web application. |
| String | getInitParameter(String name) Returns a String containing the value of the named context-wide initialization parameter, or null if the parameter does not exist. |
| String | getRealPath(String path) Returns a String containing the real path for a given virtual path |
| void | setAttribute(String name, Object object) Binds an object to a given attribute name in this servlet context. |
| InputStream | getResourceAsStream(String path) Returns the resource located at the named path as an InputStream object. |
Enumeration getInitParameterNames():
获取 web.xml 文件的中的所有的上下文参数名称。其返回值为枚举类型 Enumeration。
void setAttribute(String name, Object object):
在 ServletContext 的公共数据空间中,也称为域属性空间,放入数据。这些数据对于 Web应用来说,是全局性的,与整个应用的生命周期相同。当然,放入其中的数据是有名称的,通过名称来访问该数据。
Object getAttribute(String name):
从 ServletContext 的域属性空间中获取指定名称的数据。
void removeAttribute(String name):
从 ServletContext 的域属性空间中删除指定名称的数据。
String getRealPath(String path):
获取当前 Web 应用中指定文件或目录在本地文件系统中的路径,是基于盘符的路径。
String getContextPath():
获取当前应用在 Web 容器中的名称。
7.3 ServletContext使用
7.3.1 常规应用
Ø 获取WEB应用的全局初始化参数
Ø 通过ServletContext对象实现数据共享
key-value值:全局的
XML
<context-param>
<param-name>aaaa</param-name>
<param-value>bbbb</param-value>
</context-param>
java
ServletContext servletContext = servletConfig.getServletContext();
String aaaa = servletContext.getInitParameter("aaaa");
System.out.println("aaaa:"+aaaa);
示例:
统计站点访问次数
java
package com.qcby.servlet;
import javax.servlet.ServletContext;
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;
import java.io.PrintWriter;
@WebServlet(name = "Servlet3",urlPatterns = "/hello3")
public class Servlet3 extends HttpServlet {
private static final long serialVersionUID = 1L;
public Servlet3() {
super();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置字符编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");
//获取全局的共享数据
ServletContext servletContext = this.getServletContext();
//获取计数器count
Integer count = (Integer) servletContext.getAttribute("count");
//如果获取的计算器对象为空 ,说明是第一次访问,并将count,放入servletCount
if( servletContext.getAttribute("count") == null) {
count = 1;
servletContext.setAttribute("count", count);
}else {
//否则就不是第一次访问,将登陆的计数器进行加1的数据更新
servletContext.setAttribute("count", count+1);
}
//将登陆的次数显示在页面上
PrintWriter out =response.getWriter();
out.print("<!DOCTYPE html>\r\n" +
"<html>\r\n" +
"<head>\r\n" +
"<meta charset=\"UTF-8\">\r\n" +
"<title>登陆网页次数统计</title>\r\n" +
"</head>\r\n" +
"<body>");
out.print("<h1>");
out.print("您是第 "+ servletContext.getAttribute("count")+"位访客");
out.print("<h1>");
out.print("</body>\r\n" +
"</html>");
}
}
7.3.2 通过ServletContext对象读取资源文件
第一种方法,
通过ServletContext.getRealPath()方法
java
// 获得到ServletContext对象ServletContext
servletContext = config.getServletContext();
// 获得工程目录web下文件的绝对路径
//D:\workspace\idea\servlet-demo1\out\artifacts\servlet_demo1_war_exploded\tx.properties
// getRealPath的参数内容不会被校验,只有真正要用这个路径的时候才知道路径对不对
// String path = servletContext.getRealPath("tx.properties");
String path = servletContext.getRealPath("tx.properties");
System.out.println(path);
try {
InputStream is = new FileInputStream(path);
Properties p = new Properties();
p.load(is);
String username = p.getProperty("username");
System.out.println(username);
} catch (Exception e){
e.printStackTrace();
}
第二种方法,
使用servletContext.getResourceAsStream()方法
java
//获得工程目录web下文件的流第一个/代表项目的根目录
InputStream is = servletContext.getResourceAsStream("/WEB-INF/tx1.properties");
第三种方法,
使用getClass().getClassLoader().getResourceAsStream()方法
java
//使用类加载器的方式来读取classpath下的资源文件,好处不依赖与ServletContext,任何类都可以获得classpath下的资源文件,
//不需要再自己指定/WEB-INF/classes
InputStream is = this.getClass().getClassLoader().getResourceAsStream("tx2.properties");
8.Servlet相关资料
面试题
8.1 单例的Servlet 单例多线程
因为Servlet实例是由Tomcat来创建的,但Tomcat只会创建一个Servlet实例,所以Servlet就是单例的!这与我们自己写的单例模式不太一样。因为这种单例是通过容器tomcat来管理而实现的!
一个实例需要在同一个时间点上处理多个请求!
同步就是安全,但效率太低!
Servlet是线程不安全的!
Ø 不写属性;
Ø 不写可以存储数据的属性!
8.2 Servlet的生命周期
Ø Servlet 通过调用 init () 方法进行初始化。
Ø Servlet 调用 service() 方法来处理客户端的请求。
Ø Servlet 通过调用 destroy() 方法终止(结束)。
Ø 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。