Servlet 详解
1/Servlet 概述
1、JavaWeb 三大组件
Servlet 是 JavaWeb 三大组件之一,它是我们学习 JavaWeb 最为基本的组件,也就是说你一定要100% 的掌握它。
其它两种:Filter(拦截器)、Listener(观察者模式),后续讲解
2、Servlet 的作用
Servlet,即 Server Let 的意思,用来处理用户请求。当客户端发出请求后,由 Tomcat 去找到可以处理这一请求的 Servlet 来处理。
2/实现一个 Servlet
当我们写好了 LoginServlet 类后,还需要在 web.xml 文件中添加上面的内容。
JavaWeb 请求响应原理如下:
当 Tomcat 接收到请求(http://localhost:8080/servlet_pro/logon)后,Tomcat 会找到 servlet_pro 项目中的web.xml 文件,然后通过 logon 这个请求路径,查找处理这个请求的 Servlet 类型。这刚好与 <url-pattern>/logon</url-pattern> 匹配,这说明存在一个可以通过这个请求的 Servlet。然后在通过 <url-pattern>/logon</url-pattern> 查找到 <servlet-name>login</servlet-name>,然后再通过 <servlet-name>login</servlet-name> 查找到 <servlet-class>com.rl.servlet.LogonServlet</servlet-class> 。这时Tomcat 已经得到了一个 Servlet 类名字(一个字符串而已)。
Tomcat 通过 Servlet 类名字去查找内存中是否存在 Servlet 对象,如果存在,那么就不在去创建,直接获取这个 Servlet 实例,调用它的 Servlet() 方法完成请求!
如果这个 Servlet 不存在,那么 Tomcat 会通过反射来创建 Servlet 实例,并把 Servlet 实例存放到 Servlet 池中,再去调用 Servlet 的 service 方法处理请求。
3/Servlet 生命周期
java.servlet.Servlet 接口中,有三个方法说明了 Servlet 的生命周期;
void init (ServletConfig):创建后马上调用 init() 完成初始化;
void servlet(ServletRequest,ServletResponse):每次处理请求时调用 servlet() 方法;
void destroy():当 Tomcat 要销毁 Servlet 实例时,先调用 destroy() 方法。
Servlet 对象的实例默认情况下是在浏览器第一次调用 servlet 时候被创建的(可以修改创建时机后续讲解)
现在你应该已经清楚了,Servlet 的实例不由我们创建,Servlet 的方法不由我们来调用,这一切都是由 Tomcat 来完成!!!这就是说由 Tomcat 来管理 Servlet,而我们只需要去编写 Servlet 实现类,并将其部署到 web.xml 文件中去!
在次提醒,只有这三个方法是生命周期中的方法。也就是说,生命周期方法会被 Tomcat 在不同的时间点来调用!而其它方法就不会被调用了!!!如果你在自己写的 Servlet 中添加了其它方法,那么 Tomcat 也是不会去调用他们的!但你可以让生命周期方法调用你自己写的方法就 OK 了!
void init(ServletConfig config):默认情况当 Servlet 被第一访问的时候 init 会被调用,说明 Servlet 的实例默认情况下是第一次访问的时候创建的(可以改的),第二次往后都不会再被调用,Servlet 的实例时单例的
void service(ServletRequest req,ServletResponse res):每次请求来的时候都会被调用
void destroy():当 ServletDemo2 销毁时候被调用
4/专注 HTTP 请求的 Servlet
1、写一个专门处理 HTTP 请求的 Servlet
因为想在我们的请求都是基于 HTTP 协议的,所以我们应该专门为 HTTP 请求写一个 Servlet 作为通用父类。
对于专注于 HTTP 的Servlet,我们需要处理以下几个问题:
Servlet() 方法的参数 ServletRequest 和 ServletResponse,但因为所有的请求都是 HTTP 请求,所以传递给 Servlet() 就去的参数其实是 HttpServletRequest 和 HttpServletResponse 对象。如果子类希望使用的是 HttpServletRequest,而不是 ServletRequest ,那么它需要自己去做强转。
Servlet:SUN 公司 设计之初以后不仅仅依赖 HTTP 协议
GenericServlet --- 通用 Servlet
service(ServletRequest req, ServletResponse res)
HttpServlet --- 与协议相关的 Servlet
servlet(HttpServletRequest req, HttpServletResponse)
以后再写 Servlet 继承 HttpServlet
Servlet --- 一个标准
|
GenericServlet --- 是 Servlet 接口子类
|
HttpServlet --- 是 GenericServlet 子类,一个专门处理 Http 请求的 Servlet
HttpServlet
两个 Servlet 方法
父类 servlet 调用子类 servlet 使用子类 servlet 方法就可以
子类中 servlet 根据请求方式不同 调用不同的方法
只需要重写 doGet 和 doPost 就行
写一个 Servlet 继承 HttpServlet
重写 doGet 和 doPost 方法
没错,java 中已经存在了 java.servlet.http.httpServlet 类。打开源代码看看这个类!
2、HTTP 请求方法
Http 请求方法不只是 GET 和 POST ,还有其他的方法,但基本上用不上。这里只是简单介绍一下。你自己心里有个数,HTTP 请求除了 GET 和 POST 之外还有别的就行了。
GET 通过请求 URL 得到资源
POST 用于添加新的内容
PUT 用于修改某个内容
DELETE 删除某个内容
CONNECT 用于代理进行传输,如使用SSL
OPTIONS 询问可以执行那些方法
PATCH 部分文档更改
RACE 用于远程诊断服务器
HEAD 类似于 GET,但是不返回 body 信息,用于检查对象是否存在,以及得到对象的元数据
TRACE 用于远程诊断服务器
5/Tomcat 启动时创建 Servlet
有些 Servlet 需要在 Tomcat 启动时就被创建,而不是第一次访问时被创建,那么可以在 web.xml 文件中配置<servlet>元素。
在<servlet> 元素中添加子元素<load-on-startup>元素!
这个元素的值必须是一个大于等于 0 的整数!!!
【【【
<servlet>
<servlet-name>One</servlet-name>
<servlet-class>com.rl.serclet.OneServlet</servlet-class>
<load-on-startup>1<load-on-startup>
</servlet>
<servlet>
<servlet-name>Two<servlet-name>
<servlet-class>com.rl.servlet.TwoServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
】】】
所有添加了 <load-on startup>子元素的 Servlet,都会在 Tomcat 启动时被创建!当然,只是被创建,但没有处理请求!但是我们知道在 Servlet 生命周期中 init() 方法会在创建后被调用,所以你可以在 init() 方法中做一些输出,查看是否在 Tomcat 启动时调用了它。
<load-on-startup> 元素的值是一个序号,Tomcat 会使用这个序号给多一个 Servlet 排序!然后在 Tomcat 启动时会按这个顺序创建 Servlet 实例对象!
6/ServletConfig
ServletConfig 对象对应 web.xml 文件中的 <servlet> 元素。例如你想获取当前 Servlet 在web.xml 文件中配置的名,那么可以使用 ServletConfig.getServletName() 方法获取!
你不能自己去创建 ServletConfig 对象,Servlet 的 init() 方法的参数就是 ServletConfig 类型的。Tomcat 在调用 init() 方法时,会传递 ServletConfig 对象。你可以在 init() 方法中使用它!
在<servlet> 元素中还可以配置初始化参数:
【【【
<servlet>
<servlet-name>One</servlet-name>
<servlet-class>com.rl.servlet.OneServlet</servlet-class>
<init-param>
<param-name>paramName1</param-name>
<param-value>paramValue1</param-value>
</init-param>
<init-param>
<param-name>paramName2</param-name>
<param-value>paramValue2</param-name>
</init-param>
</servlet>
】】】
添加了两个初始化参数,第一参数的名称为 paramName1,第一个参数的值为 paramValue1;第二个参数的名称为 paramName2,第二个参数值为 paramValue2。
在<servlet>元素中可以加载多个<init-param>,每个<init-param>表示一个参数。<init-param>下有两个子元素:<param-name>和<param-value>,其中<param-name>表示参数的名称,而<param-value>元素参数的值。
注意,<init-param>是添加到<servlet>元素中,而不是<servlet-mapping>中。
使用 ServletConfig 对象的 getInitparameter(String paramName) 方法可以获取指定参数名称的参数值。getInitParameterName() 方法返回所有参数的名字,返回值类型为 Enumeration。
7/Servlet 路径映射
关于 Url-Pattern 的配置:
1.完全路径匹配 以/开头 例如 /aaa /aaa/bbb
2.目录匹配 以/开头 例如 /aaa/* /*
3.扩展名匹配 不能以/开头 例如 *.do *.action ……
优先级:完全路径匹配 > 目录匹配 > 扩展名匹配
经典错误:/*.do
举例:
对于如下的一些映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.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
8/ServletContext
web 容器在启动时,它会为每个 WEB 应用程序都创建一个对应的 ServletContext 对象,它代表当前 web 应用。
由于一个 WEB 应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 servlet 对象之间可以用过 ServletContext 对象来实现通讯。ServletContext 对象通常也被称之为 context 域对象。
查看 ServletContext API 文档,了解 ServletContext 对象的功能。
应用:
获取 WEB 应用的全局初始化参数
通过 ServletContext 对象实现数据共享
案例 ———— 统计站点访问次数
利用 ServletContext 对象读取资源文件
【【【
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 nuli if the parameter does not exist.
String getRealPath(String path)
Returns a String contining 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.
】】】
9/Servlet 单例和线程安全问题
因为 Servlet 实例是由 Tomcat 来创建的,但 Tomcat 只会创建一个 Servlet 实例,所以 Servlet 就是单例的!这与我们自己写的单例模式不太一样。因为这种单例是通过容器来管理而实现的!
一个实例需要在同一个时间点上处理多个请求!
同步就是安全,但效率太低!
Servlet 是线程不安全的!
不写属性;
不写可以存储数据的属性!
10/习题
HttpServlet 和 Servlet 是什么关系
实际开发 Servlet 的步骤是什么
什么是 Servlet 生命周期
init(ServletConfig) 方法什么时候执行
<load-on-startup>
为什么要配置 Servlet 的 url-pattern
Servlet 路径映射有几种配置方式
ServletConfig 和 ServletContext 的区别