JAVAWeb-Serlet学习笔记
Servlet
Servlet和Tomcat的关系
Tomcat 和 Servlet 的关系:Servlet 的运行环境叫做 Web 容器或 Servlet 服务器,Tomcat 是 Web 应用服务器,是一个 Servlet/JSP 容器。Tomcat 作为 Servlet 容器,负责处理客户请求,把请求传送给 Servlet,并将 Servlet 的响应传送回给客户。而 Servlet 是一种运行在支持 Java 语言的服务器上的组件,Servlet 用来扩展 Java Web 服务器功能,提供非常安全的、可移植的、易于使用的 CGI 替代品
执行过程
通过浏览器发送请求,请求首先到达Tomcat服务器,由服务器解析请求URL,然后在部署的应用列表中找到应用。然后找到web.xml配置文件,在web.xml中找到FirstServlet的配置(
实现方式
实现 Servlet 功能时,可以选择以下三种方式:
- 第一种:实现 Servlet 接口,接口中的方法必须全部实现。
使用此种方式,表示接口中的所有方法在需求方面都有重写的必要。此种方式支持最大程度的自定义。 - 第二种:继承 GenericServlet,service 方法必须重写,其他方可根据需求,选择性重写。
使用此种方式,表示只在接收和响应客户端请求这方面有重写的需求,而其他方法可根据实际需求选择性重写,使我们的开发Servlet变得简单。但是,此种方式是和 HTTP 协议无关的。 - 第三种:继承 HttpServlet,它是 javax.servlet.http 包下的一个抽象类,是 GenericServlet 的子类。选择继承 HttpServlet 时,需要重写 doGet 和 doPost 方法,来接收 get 方式和 post 方式的请求,不要覆盖 service 方法。使用此种方式,表示我们的请求和响应需要和 HTTP 协议相关,我们是通过 HTTP 协议来访问。每次请求和响应都符合 HTTP 协议的规范。请求的方式就是 HTTP 协议所支持的方式(GET POST PUT DELETE TRACE OPTIONS HEAD )。
异步处理
Servlet 3.0 中的异步处理指的是允许Servlet重新发起一条新线程去调用 耗时业务方法,这样就可以避免等待
生命周期
servlet从创建到销毁的过程:
-
出生:(初始化)请求第一次到达 Servlet 时,创建对象,并且初始化成功。Only one time
-
活着:(服务)服务器提供服务的整个过程中,该对象一直存在,每次只是执行 service 方法
-
死亡:(销毁)当服务停止时,或者服务器宕机时,对象删除,
serrvlet生命周期方法:
init(ServletConfig config)
→ service(ServletRequest req, ServletResponse res)
→ destroy()
默认情况下, 有了第一次请求, 会调用 init() 方法进行初始化【调用一次】,任何一次请求,都会调用 service() 方法处理这个请求,服务器正常关闭或者项目从服务器移除, 调用 destory() 方法进行销毁【调用一次】
扩展:servlet 是单例多线程的,尽量不要在 servlet 里面使用全局(成员)变量,可能会导致线程不安全
- 单例:Servlet 对象只会创建一次,销毁一次,Servlet 对象只有一个实例。
- 多线程:服务器会针对每次请求, 开启一个线程调用 service() 方法处理这个请求
线程安全
Servlet运用了单例模式,整个应用中只有一个实例对象,所以需要分析这个唯一的实例中的类成员是否线程安全
1 | public class ServletDemo extends HttpServlet{ |
启动两个浏览器,输入不同的参数(http://localhost:8080/ServletDemo/username=aaa 或者bbb),访问之后发现输出的结果都是一样,所以出现线程安全问题。
在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,我们可以认为Servlet它不是线程安全的。因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。
解决办法:如果类成员是共用的,只在初始化时赋值,其余时间都是获取。或者加锁synchronized
创建方式
- 第一种:应用加载时创建Servlet,它的优势是在服务器启动时,就把需要的对象都创建完成了,从而在使用的时候减少了创建对象的时间,提高了首次执行的效率。它的弊端是在应用加载时就创建了Servlet对象,因此,导致内存中充斥着大量用不上的Servlet对象,造成了内存的浪费。
- 第二种:请求第一次访问是创建Servlet,它的优势就是减少了对服务器内存的浪费,因为一直没有被访问过的Servlet对象都没有创建,因此也提高了服务器的启动时间。而它的弊端就是要在应用加载时就做的初始化操作,它都没法完成,从而要考虑其他技术实现。
在web.xml中是支持对Servlet的创建时机进行配置的,配置的方式如下:
1 | <!--配置ServletDemo3--> |
ServletContext
ServletContext 对象是应用上下文对象。服务器为每一个应用都创建了一个 ServletContext 对象,ServletContext 属于整个应用,不局限于某个 Servlet,可以实现让应用中所有 Servlet 间的数据共享。
上下文代表了程序当下所运行的环境,联系整个应用的生命周期与资源调用,是程序可以访问到的所有资源的总和,资源可以是一个变量,也可以是一个对象的引用
生命周期:
- 出生:应用一加载,该对象就被创建出来。一个应用只有一个实例对象(Servlet 和 ServletContext 都是单例的)
- 活着:只要应用一直提供服务,该对象就一直存在。
- 死亡:应用被卸载(或者服务器停止),该对象消亡。
域对象:指的是对象有作用域,即有作用范围,可以实现数据共享,不同作用范围的域对象,共享数据的能力不一样。
Servlet 规范中,共有4个域对象,ServletContext 是其中一个,web 应用中最大的作用域,叫 application 域,可以实现整个应用间的数据共享功能。
数据共享:
获取ServletContext:
-
Java 项目继承 HttpServlet,HttpServlet 继承 GenericServlet,GenericServlet 中有一个方法可以直接使用
1
2
3public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
} -
ServletRequest 类方法:
1
ServletContext getServletContext()//获取ServletContext对象
常用API:
String getInitParameter(String name)
: 根据名称获取全局配置的参数String getContextPath
: 获取当前应用访问的虚拟目录String getRealPath(String path)
: 根据虚拟目录获取应用部署的磁盘绝对路径void setAttribute(String name, Object object)
: 向应用域对象中存储数据Object getAttribute(String name)
: 根据名称获取域对象中的数据,没有则返回nullvoid removeAttribute(String name)
: 根据名称移除应用域对象中的数据
代码实现:
- web.xml配置:
配置的方式,需要在<web-app>
标签中使用<context-param>
来配置初始化参数,它的配置是针对整个应用的配置,被称为应用的初始化参数配置。
Servlet和Spring的Service层接口的关系
一个Service接口(方法)就是一个Servlet(实例)
示例:设定具体 Controller,控制层 java / controller / UserController
1 | //@Component衍生注解 |
所以 一个方法就是一个Servlet实例