目录
1. Servlet概述(了解)
1.1. JavaWeb的三大组件
1.2. Servlet的作用
2. Servlet初识(熟练)
2.1. 第一个Servlet
2.1.1. Servlet说明
2.1.2. Servlet接口
2.1.3 创建Servlet
2.1.4 JavaWeb请求响应流程
2.2 Servlet生命周期
3.HttpServlet
3.1 HttpServlet介绍
3.2 Http请求方法
3.3 创建HttpServlet
3.3.1 第一种方法
3.3.2 第二种方法
3.3.3 Servlet创建顺序
4.ServletConfig
5.Servlet路径映射
6.相对路径和绝对路径
6.1 相对路径
6.2 绝对路径
7.ServletContext
7.1 ServletContext介绍
7.2 ServletContext API
7.3 ServletContext使用
7.3.1 常规应用
7.3.2 通过ServletContext对象读取资源文件
8.Servlet相关资料
8.1 单例的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接口
Ø 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的访问路径
<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的映射路径
<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 可以确保这些资源在应用启动后就可用。
<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()方法中使用它!
<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值:全局的
<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 的垃圾回收器进行垃圾回收的。