宏观地讲,Servlet 是连接 Web 服务器与服务端 Java 程序的协议,是一种通信规范。这个规范是以一套接口的形式体现的。微观地讲,Servlet 是 Servlet 接口实现类的一个实例对象,是运行在服务器上的一段 Java 小程序,即 Server Applet,也就是 Servlet 这个单词的来历。Servlet 的主要功能是根据客户 端提交的请求,调用服务器端相关 Java 代码,完成对请求的处理与运算。
所谓 Servlet生命周期是指,Servlet对象的创建、Servlet 对象的初始化、Servlet对象服务的执行,及最终 Servlet 对象被销毁的整个过程。
Servlet 的整个生命周期过程的执行,均由Web服务器负责管理。即Servlet从创建到服务到销毁的整个过程中方法的调用,都是由Web服务器负责调用执行,程序员无法控制其执行流程。
Servlet 生命周期方法的执行流程: (1)当请求发送到 Web 容器后,Web 容器会解析请求 URL,并从中分离出 Servlet 对应的URI。 (2)根据分离出的 URI,通过web.xml中配置的URI与Servlet的映射关系,找到要执行的Servlet,即找到用于处理该请求的 Servlet。 (3)若该 Servlet 不存在,则调用该Servlet的无参构造器、init()方法,实例化该Servlet。然后执行service()方法。 (4)若该 Servlet 已经被创建,则直接调用 service()方法。 (5)当 Web 容器被关闭,或该应用被关闭,则调用执行 destroy()方法,销毁 Servlet 实例。
(1)Servlet 是单例多线程的。 (2)一个 Servlet 实例只会执行一次无参构造器与 init()方法,并且是在第一次访问时执行。 (3)用户每提交一次对当前 Servlet 的请求,就会执行一次 service()方法。 (4)一个 Servlet 实例只会执行一次 destroy()方法,在应用停止时执行。 (5)由于 Servlet 是单例多线程的,所以为了保证其线程安全性,一般情况下是不为Servlet类定义可修改的成员变量的。因为每个线程均可修改这个成员变量,会出现线程安全问题。 (6)默认情况下,Servlet 在 Web 容器启动时是不会被实例化的。
当 Servlet 实例被创建好后被放在了哪里?web.xml中URI与Servlet的映射关系反映到内存中是以什么形式存在呢?
当 Servlet 实例被创建好后,会将该Servlet实例的引用存放到一个 Map 集合中。该Map集合的 key 为 URI,而value则为Servlet实例的引用,即Map<String, Servlet>。当Web容器从用户请求中分离出 URI 后,会首先到这个Map中查找是否存在其所对应的 value。若存在,则直接调用其service()方法。若不存在,则需要创建该Servlet实例。
若请求的 Servlet实例不存在,Web容器又是根据什么创建这个Servlet实例的呢?在Web容器的内存中,还存在一个 Map 集合。该 Map 集合的key为 URI,而 value 则为 web.xml中配置的与之对应的 Servlet 的全限定性类名,即Map<String,String>。 当 Web 容器从用户请求中分离出URI后,到第一个 Map 中又没有找到其所对应的Servlet实例,则会马上查找这第二个Map,从中找到其所对应的类名,再根据反射机制,创建这个Servlet实例。然后再将这个创建好的 Servlet 的引用放入到第一个 Map 中。
这种情况下的servlet时序图 两个map
ServletContext对象:servlet容器在启动时会加载web应用,并为每个web应用创建唯一的servlet context对象,可以把ServletContext看成是一个Web应用的服务器端组件的共享内存,在ServletContext中可以存放共享数据。ServletContext对象是真正的一个全局对象,凡是web容器中的Servlet都可以访问。
这个 Servlet运行环境中都包含哪些具体的“环境”呢?即 ServletContext对象中都包含哪些具体的信息呢?不仅包含了web.xml文件中的配置信息还包含了当前应用中所有Servlet可以共享的数据可以这么说,ServeltContext可以代表整个应用。所以,ServletConetxt有另外一个名称:application。
在通过实现 Servlet接口来定义Servlet时存在一个很不方便的问题:有太多不需要的方法必须要实现。通常我们只关心service()方法,在service()方法中完成业务逻辑,但由于Servlet接口中还存在另外四个方法,所以也要必须实现。
由于 Servlet中通常只使用service()方法,其它四个方法基本不用,但也需要实现,于是我们就想,能否定义一个通用的抽象类,让其实现 Service 接口,并以简单方式对service()以外的其它方法进行实现,即要么是空方法体,要么返回null,而将 service()方法声明为抽象方法。这样,以后再定义 Servlet时就只需要继承自这个通用的抽象类即可,无需再实现了Service 接口了。 代码实现:
public abstract class MyGenericServlet implements Servlet, ServletConfig {
private ServletConfig config;
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
public ServletConfig getServletConfig() {
return config;
}
// 抽象方法,专门让子类实现
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
public String getServletInfo() {
return "";
}
public void destroy() {
}
public String getServletName() {
return getServletConfig().getServletName();
}
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
public Enumeration<String> getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
}
若GenericServlet
的子类重写init
方法,则子类必须调用父类的init
方法,来初始化config
参数。
解决方法:模板设计模式 在GenericServlet中添加无参init方法,专门用来让子类重写。
// 更改部分GenericServlet代码
public void init(ServletConfig config) throws ServletException {
this.config = config;
// 调用无参方法
this.init();
}
/**
* 专门让子类重写该方法
*/
public void init() {
}
public class SomeServlet extends MyGenericServlet {
@Override
public void init() {
System.out.println("SomeServlet init method");
}
}
在编译期检查GernericServlet中有没有init()方法,在运行时调用SomeServlet里的init()方法。
javax.servlet.http.HttpServletRequest是SUN制定的Servlet规范,是一个接口,表示请求,其父接口是javax.servlet.ServletRequest。“HTTP 请求协议”的完整内容都被封装到request对象中。 Apache 软件基金会开发的“Tomcat容器”对javax.servlet.http.HttpServletRequest接口进行了实现,其实现类完整类名是org.apache.catalina.connector.RequestFacade
HttpServletRequest实例对象是什么时候创建和销毁的呢? 当客户端浏览器将请求(字符序列)发送到服务器后,服务器会根据HTTP请求协议的格式对请求进行解析。同时,服务器会创建 HttpServletRequest 的实现类 RequestFacade的对象,即请求对象。然后再调用相应的set方法,将解析出的数据封装到请求对象中。此时HttpServletRequest实例就创建并初始化完毕了。也就是说,请求对象是由服务器创建。 当服务器向客户端发送响应结束后,HttpServletRequest 实例对象被服务器销毁。 一次请求对应一个请求对象,另外一次请求对应外一个请求对象,与之前的请求对象没有任何关系。HttpServletRequest 实例的生命周期很短暂。
在接收请求参数之前先通过request的setCharacterEncoding()方法,指定请求体的字符编码格式。
// 设置请求体的编码方式
request.setCharacterEncoding("UTF-8");
String name = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username: " + name);
System.out.println("password: " + password);
tomcat9已经解决该问题,若是之前的版本可以在conf/server.xml文件中,配置connector标签解决。
从数据在请求中的存放形式,到数据被 Tomcat 中的 Servlet 接收到后的存放形式,均是 由单个字节的形式存在,而在众多字符编码格式中,ISO8859-1 为单字节编码,所以,首先 以ISO8859-1 的形式先对单字节的数据进行编码,并将编码后的数据存放在字节数组中。然后,再将字节数组中的数据,按照指定的 UTF-8 格式进行解码,即变为了需要的 UTF-8 字符编码的数据,解决了中文乱码问题。
String name = request.getParameter("username");
String password = request.getParameter("password");
// 编码
byte[] bytes = name.getBytes("ISO8859-1");
// 解码
name = new String(bytes, "UTF-8");
System.out.println("username: " + name);
System.out.println("password: " + password);
若在 PrintWriter流中写入中文字符,那么,在客户端浏览器中将显示乱码。例如,本例中的username 在表单中填入汉字,则将显示乱码。只所以响应时会产生乱码,是因为HTTP协议中规定,默认响应体的字符编码为ISO-8859-1。所以,若要解决乱码问题,就需要修改响应体的默认编码。一般情况下,有两种方式可以修改:
HttpServletResponse的setContentType(“text/html;charset=utf-8”)方法,用于设置响应内容的MIME类型,其中可以指定MIME的字符编码。而MIME的字符编码,即响应体的字符编码。
setCharacterEncoding(“utf-8”)方法,用于修改MIME 的字符编码,即修改响应体的字符编码。但使用 setCharacterEncoding()方法的前提是,之前必须要通过使用方法setContentType()方法设置响应内容的 MIME类型。否则setCharacterEncoding()方法不起作用。
注意: 这些设置,必须在PrintWriter对象产生之前先设置,否则将不起作用。
实际应用中实现了 JavaEE规范的Web服务器很多,如 Oracle 的 WebLogic(需购买)、IBM的WebSphere(需购买)、RedHat公司的JBoss(不支持Servlet/JSP 规范,开源免费)等。这些都称为重量级服务器。
Apache 的 Tomcat,只是JavaEE规范中Servlet/JSP规范的实现者,所以其是一个轻量级服务器,它是开源免费的。我们现在的学习过程,使用的是 Tomcat 服务器。Tomcat也称为Web容器,或Servlet容器,但不能称为 JavaEE 容器。
参考链接: [Tomcat安装、配置、优化及负载均衡详解][1]
![目录结构][2]
Tomcat 安装完毕后,需要安装JDK或JRE。对于Tomcat5 及以前版本,要求必须要安装JDK,而Tomcat6 及其后版本可以只安装JRE 而不安装JDK。
目前我安装的JDK是8.0,在系统环境变量中设置JAVA_HOME 后,将其bin目录添加到path变量中即可。 现在不需要做CATALINA配置,因为在startup.bat里面已经进行了配置
运行startup.bat文件就可以运行tomcat。成功运行之后可以进行本机访问。
默认端口是8080,改端口:tomcat的conf/server.xml
的Connector
标签,若端口号改为80,则在浏览器中不用输入端口号
注:运行期间出现的问题
// 找到占用8080端口的PID
netstat -ano | findstr 8080
// 关闭相应进程
taskkill -pid {进程pid} -f
start
改为run
可以避免重新打开一个窗口,可以看到错误日志通过 http://127.0.0.1:8080,可以进行访问。127.0.0.1 称为回送地址
,表示本机。无论本机是否连接网络,均可进行访问。
通过 http://localhost:8080可以进行访问。localhost 是本地DNS解析的127.0.0.1的域名,打开本机的名称为 hosts文件就可以看到。该文件在 Win10 系统中一般位于如下位置:
C:\Windows\System32\drivers\etc\
![host文件][3]
JCP,Java Community Process,Java 审核社区,是一个开放的国际组织,主要负责规范、监督 Java 的发展。其他个人、企业、机构等制定的 Java 规范,必须通过JCP审核后,才可被认定为 Java 规范。JCP 的创始者为 SUN 公司。 JCP 官网为:http://jcp.org。
HelloWorld---------------------------Web应用所在目录
|----html、jsp、css、js等文件,根目录下的文件外界可以直接访问
|----WEB-INF目录
|---------classes目录(java类)
|---------lib目录(java类运行所需的jar包)
|---------web.xml(web应用的配置文件)
WEB-INF 这个目录下的文件外界无法直接访问,由web服务器负责调用
输入的打包命令为:
jar cvfd:\abc\myprimary.war .
cvf
是命令参数,表示生成一个文档、显示生成过程、指定生成的文件名。d:\abc\myprimary.war
为生成的文档的存放路径及文件名。.
表示要将当前目录中所有内容进行打包.将war包放在webapps目录下,tomcat启动时会自动解压war包并发布
在eclipse中创建dynami JavaWeb项目并运行后,因为项目是在eclipse里的tomcat的副本里运行的,所以tomcat目录下的webapps下没有。项目发布在以下目录:
{workspace}\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps
这个目录是由以下路径中的server.xml
里的deployDir="wtpwebapps"
字段决定
Tomcat 的核心配置都集中在 Tomcat 安装目录的 conf\server.xml 文件中。 ![tomcat组织结构][4]
<Server>
<Service>
<Connector/>
<Engine>
<Host>
<Context>
</Host>
</Engine>
</Service>
</Server>
Server
代表整个Servlet容器组件,是最顶层元素,可以包含一个或多个Service
元素
Service
包含一个Engine
元素以及一个或多个Connector
元素,这些Connector
共享一个Engine
Connector
代表和客户程序实际交互的组件,负责接收客户请求,以及向客户返回响应
Engine
每个Service
元素只能包含一个Engine
元素,它处理在同一个Service
中所有Connector
接收到的客户请求
Host
在一个Engine
中可以包含多个Host
,它代表一个虚拟主机(即一个服务器程序可以部署在多个有不同IP的服务器主机上),它可以包含一个或多个应用
Context
使用最频繁的元素,代表了运行在虚拟主机上的单个web应用
默认情况下,主机会将appBase指定目录中的所有项目通过当前主机域名访问。但也可以将本地文件系统中任意目录指定为通过该机域名访问的应用目录,交由Tomcat进行管理。这个被指定的文件系统中的目录,称为虚拟目录
。
有两种方式可以实现对虚拟目录的配置。
Context
标签<Context path="/virtual-path" docBase="D:/helloworld2"/>
Tomcat 启动后,会在 Tomcat 安装目录的 conf 中自动创建一个目录。该目录与server.xml的Engine
标签对应,目录名与
需要注意的是,该Context
标签没有path属性。该 path 属性值即为该XML文件的文件名。也就是说,该XML文件名即为在当前主机域名下访问该应用的URI。该方式所创建的虚拟目录无需重启Tomct,就可以直接由Tomcat来管理。当这个XML文件写完后一保存,即可发现Tomcat的控制台信息中提示,已经成功发布该项目。
配置虚拟主机,即在同一个硬件主机上运行多个域名系统,当作多个主机来使用,即在同一台机器上虚拟出多个主机。换句话说,在同一个Tomcat上,除了运行着默认的localhost域名系统外,同时还运行着其它域名的系统。
只所以我们通过域名locahost可以访问本机,是因为我们 Windows系统本身也充当前DNS服务器的角色。打开C:/Windows/System32/drivers/etc
中的 hosts
文件,可以看到 localhost域名所对应的 IP 地址为 127.0.0.1,即本地回送地址,表示本机。
servlet.xml
中Host
后添加另一个Host
主机,指定该主机的域名为www.xiaocorn.com。mywebapps
,并向里面添加工程文件。hosts
文件,向里面添加以下映射
127.0.0.1 www.xiaocorn.com
结果:
{tomcatroot}\conf\Catalina
中会有新的目录www.xiaocorn.com
,代表新的虚拟主机,并且可以访问www.xiaocorn.com:8080/helloworld
由浏览器向服务器发送数据,称为请求Request。由服务器向浏览器发送数据,则称为响应Response。那么,什么才是浏览器向服务器发送的请求呢?在浏览器地址栏中通过地址访问是最典型的请求方式,另外还有点击表单的提交按钮、点击超链接、发送 AJAX 请求。
// TODO 那么还有其它形式的请求吗?
参考博文 http协议无状态中的”状态”到底指的是什么?! http协议和web应用有状态和无状态浅析
HTTP1.0 协议中的连接属于非持久连接,且服务器不跟踪和记录任何一次请求与响应。
HTTP 1.1 版本是目前浏览器默认采用的HTTP协议版本,是一种持久连接,在一个TCP连接上可以传送多个 HTTP 请求和响应,减少了建立和关闭连接的消耗和延迟。一个包含有许多CSS、JS、图片等资源的页面,其所发出的多个请求和响应可以在一个连接中传输。但每个单独的页面文件的请求和响应仍然需要使用各自的连接。
HTTP1.0 在客户端接收到服务端发送来的响应后,TCP 连接马上关闭。而HTTP1.1的连接是什么时候关闭呢?客户端在发送创建TCP连接请求之前首先计算出本次连接浏览器所要发送的请求数量,即一次手工请求加上其所携带的所有自动请求数量。当所有浏览器所发出的请求全部发送完毕后,客户端会再自动发送一个关闭TCP连接的请求。这个请求在HttpWatch 中是看不到的。(通过请求头的connecton
参数控制, keep-alive
, close
)
HOST
属性,用于记录请求所要访问的虚拟域名。当然,请求中所携带的域名,肯定会通过 DNS 将其转换为IP,然后查找到相应的主机。但由于请求中还携带有HOST
属性,即要访问的域名仍然在请求中,这样的话,服务器就可以从请求中解析出请求所要访问的虚拟主机名。状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别: 1xx:指示信息–表示请求已接收,继续处理 2xx:成功–表示请求已被成功接收、理解、接受 3xx:重定向–要完成请求必须进行更进一步的操作 4xx:客户端错误–请求有语法错误或请求无法实现 5xx:服务器端错误–服务器未能实现合法的请求
常见状态码:
200 OK //客户端请求成功 400 Bad Request //客户端请求有语法错误,不能被服务器所理解 401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 403 Forbidden //服务器收到请求,但是拒绝提供服务 404 Not Found //请求资源不存在,eg:输入了错误的URL 500 Internal Server Error //服务器发生不可预期的错误 503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常 参考资料:请求码
根据HTTP标准,HTTP请求可以使用多种请求方法。 HTTP1.0定义了三种请求方法:GET,POST和HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS,PUT,DELETE, TRACE 和 CONNECT 方法。
GET 请求指定的页面信息,并返回实体主体。 HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 PUT 从客户端向服务器传送的数据取代指定的文档的内容。 DELETE 请求服务器删除指定的页面。 CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 OPTIONS 允许客户端查看服务器的性能。 TRACE 回显服务器收到的请求,主要用于测试或诊断。
由于 GET 请求会将请求所携带的参数作为请求URL中的一部分出现,所以请求参数会显示在地址栏。而这就导致了 GET 提交的三点不足。
但 GET 请求有一个很重要的特征:客户端一旦接收到“服务器向GET请求发送的响应”后,浏览器会自动缓存响应。当客户再次进行相同请求提交时将直接读取本地浏览器缓存中数据,而不再向服务端真正发送数据,让用户感觉服务端的响应很快,提升用户体验,减轻了服务器压力。 参考博客: HTTP请求 浏览器缓存 http缓存详解
POST 请求会将请求所携带的数据以请求正文的形式出现,所以与GET方式相比,就显示出了三点长处:
POST提交的安全性比GET高,因为能够实现POST提交的方式只有两种:通过表单的POST提交,与通过 AJAX 的 POST提交。其它方式均为GET提交方式。对于一个提供了POST登录页面的系统,若用户试图通过地址栏等方式进行登录,则说明其一定是非法登录。
(粗体为平时不常用到的) Ctrl + N:新建一个工程、文件、文件夹等内容 Alt + 带下划线字母:在对话框中进行的快捷操作 Ctrl + M:窗口最大化/还原 Alt + Enter:打开被选中的工程、包、文件等的属性窗口 Ctrl + F11:运行程序 F11:以调试模式运行程序 Alt + Shift +R:重命名工程名、文件名、方法名、变量名。 F2:重命名工程名、文件名。
Ctrl + E:打开编辑窗口查看目录 Ctrl + Page Up :切换到当前编辑窗口的上一个窗口 Ctrl + Page Down:切换到当前编辑窗口的下一个窗口
Ctrl + D:删除选中行或当标所在行
Alt + Shift + Z:对选中代码段进行 Surrount With Alt + Shift + S:弹出 Source 菜单带下划线字母或数字:在选择菜单项时的快捷操作
Alt + Shift + M:将选中内容抽取为方法 Alt + Shift + L:将选中内容抽取为变量
快速选择(Windows 快捷键): Ctrl + Shift + 或 :快速选择一个单词 Shift + Home 或 End:从光标当前位置,快速选择到行首或行尾 Shift + 向上 或 向下箭头:从光标当前位置,快速选择到上一行或下一行
Ctrl + T:查看类的继承、实现关系 Ctrl + O:查看类的结构 Ctrl + Shift + T:打开查看类窗口 F3:转到定义。将光标放在类或接口或方法上,查看其定义,或按住 Ctrl,单击该类或接口 Alt + <- :返回上一次光标停留位置,即使在不同文件中
具有最大的访问权限,可以访问任何一个在classpath下的类、接口、异常等。它往往用于对外的情况,也就是对象或类对外的一种接口的形式。
有时候也称为friendly,它是针对本包访问而设计的,任何处于本包下的类、接口、异常等,都可以相互访问,即使是父类没有用protected修饰的成员也可以。
访问权限仅限于类的内部,是一种封装的体现,例如,大多数成员变量都是修饰符为private的,它们不希望被其他任何外部的类访问。
主要的作用就是用来保护子类的。它的含义在于子类可以用它修饰的成员,其他的不可以,它相当于传递给子类的一种继承的东西
- | 类内部 | 本包 | 子类 | 外部包 |
---|---|---|---|---|
public | yes | yes | yes | yes |
protected | yes | yes | yes | no |
default | yes | yes | no | no |
private | yes | no | no | no |
Servlet 规范中已经定义好了八个监听器接口,它们要监听的对象分别是request
、session
、servletContext
对象,触发监听器的事件是这三个对象的创建与销毁,它们的域属性空间中属性的添加、删除、修改,及 session 的钝化与活化操作。
在 JavaWeb 项目中使用监听器,需要在 web.xml 文件中对监听器进行注册。
public class MyRequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("请求对象被销毁");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("请求对象被创建");
}
}
<listener>
<listener-class>love.minmin.servletListener.MyRequestListener</listener-class>
</listener>
一下例子只展示监听器代码,注册部分省略
该监听器用于完成对 request 域属性空间中属性的添加、修改、删除操作的监听。
public class MyRequestAttributeListener implements ServletRequestAttributeListener {
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("add attribute: " + srae.getName() + "=" + srae.getValue());
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("remove attribute: " + srae.getName() + "=" + srae.getValue());
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("replace attribute: " + srae.getName() + "=" + srae.getValue());
}
}
该监听器用于完成对Session对象的创建及销毁的监听。
public class MyRequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("请求对象被销毁");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("请求对象被创建");
}
}
session在服务器销毁的两种方式
2. 在jsp文件里通过invalidate方法销毁
```jsp
session.invalidate();
该监听器用于完成对session域属性空间中属性的添加、修改、删除操作的监听。
public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener{
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
System.out.println("session attribute added: " + se.getName() + "=" + se.getValue());
}
@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
System.out.println("session attribute removed: " + se.getName() + "=" + se.getValue());
}
@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
System.out.println("session attribute replaced: " + se.getName() + "=" + se.getValue());
}
}
该监听器用于完成对ServletContext对象的创建及销毁的监听。不过需要注意,由于ServletContext在一个应用中只有一个,且是在服务器启动时创建。另外,ServletConetxt的生命周期与整个应用的相同,所以当项目重新部署,或Tomcat正常关闭(通过 stop service关闭,不能是terminate关闭)时,可以销毁 ServletContext。
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("servletContext initialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("servletContext destroyed");
}
}
public class MyServletContextAttributeListener implements ServletContextAttributeListener{
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("servletContext attribute added: " + scae.getName() + "=" + scae.getValue());
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("servletContext attribute removed: " + scae.getName() + "=" + scae.getValue());
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("servletContext attribute replaced: " + scae.getName() + "=" + scae.getValue());
}
}
该监听器用于监听指定类型对象与Session的绑定与解绑,即该类型对象被放入到Session 域中,或从 Session 域中删除该类型对象,均会引发该监听中相应方法的执行。
它与HttpSessionAttributeListener的不同之处是,该监听器监听的是指定类型的对象在Session 域中的操作,而HttpSessionAttributeListener监听的是 Session域属性空间的变化,无论是什么类型的对象。
注意:
// 把当前类的对象绑定到Session时触发
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("student instance bind to session");
System.out.println("getName: " + event.getName());
System.out.println("getValue: " + event.getValue());
System.out.println("----------------------------------");
}
// 把当前类的对象与Session时触发
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("student instance unbind with session");
System.out.println("getName: " + event.getName());
System.out.println("getValue: " + event.getValue());
System.out.println("----------------------------------");
}
<%
Student stu = new Student("minmin", 18);
session.setAttribute("stu", stu);
session.removeAttribute("stu");
%>
该监听器用于监听在Session中存放的指定类型对象的钝化与活化。
钝化是指将内存中的数据写入到硬盘中,而活化是指将硬盘中的数据恢复到内存。当用户正在访问的应用或该应用所在的服务器由于种种原因被停掉,然后在短时间内又重启,此时用户在访问时Session 中的数据是不能丢掉的,在应用关闭之前,需要数据写入到硬盘,在重启后应可以立即重新恢复Session 中的数据。这就称为 Session 的钝化与活化。
那么 Session中的哪些数据能够钝化呢?只有存放在 JVM 堆内存中的实现了Serializable类的对象能够被钝化。也就是说,对于字符串常量、基本数据类型常量等存放在JVM方法区中常量池中的常量,是无法被钝化的。
对于监听 Session中对象数据的钝化与活化,需要注意以下几点: