本节内容
1.监听器
2.过滤器
一、监听器
1.监听器概念:
监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。监听器其实就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法立即被执行
2.Javaweb中的监听器
JavaWeb中的监听器是Servlet规范中定义的一种特殊类,它用于监听web应用程序中的ServletContext, HttpSession和 ServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。
监听器分类:
在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为ServletContext,HttpSession和ServletRequest这三个域对象
Servlet规范针对这三个对象上的操作,又把多种类型的监听器划分为三种类型:
- 监听域对象自身的创建和销毁的事件监听器。
- 监听域对象中的属性的增加和删除的事件监听器。
- 监听绑定到HttpSession域中的某个对象的状态的事件监听器。
创建与销毁的监听器:
监听ServletContext域对象的创建和销毁
ServletContextListener接口用于监听ServletContext对象的创建和销毁事件。实现了ServletContextListener接口的类都可以对ServletContext对象的创建和销毁进行监听。
ServletContext域对象创建和销毁时机:
创建:服务器启动针对每一个Web应用创建ServletContext
销毁:服务器关闭前先关闭代表每一个web应用的ServletContext
package m.xk.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** 作者:吴志龙 日期:2018年8月23日 **/ public class ApplicationListener implements ServletContextListener{ //监听ServletContext销毁的方法 @Override public void contextDestroyed(ServletContextEvent arg0) { // TODO Auto-generated method stub System.out.println("ServletContext对象销毁"); } //监听ServletContext对象的创建方法 @Override public void contextInitialized(ServletContextEvent arg0) { System.out.println("ServletContext对象创建"); } }
监听器需要注册:
我们在上面的中讲到,要想监听事件源,那么必须将监听器注册到事件源上才能够实现对事件源的行为动作进行监听,在JavaWeb中,监听的注册是在web.xml文件中进行配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>WebDemo1</display-name> <!-- 注册针对ServletContext对象进行监听的监听器 --> <listener> <!--监听器的描述 --> <description>application对象监听器</description> <!--监听器类 全类名 --> <listener-class>m.xk.listener.ApplicationListener</listener-class> </listener> <!-- 注册针对HttpSession对象进行监听的监听器 --> <listener> <description>session监听器</description> <listener-class>m.xk.listener.SessionListener</listener-class> </listener> <!-- 配置HttpSession对象的销毁时机 --> <session-config> <!--配置HttpSession对象的1分钟之后销毁 --> <session-timeout>1</session-timeout> </session-config> <!--注册针对ServletRequest对象进行监听的监听器--> <listener> <description>request监听器</description> <listener-class>m.xk.listener.RequestListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
经过这两个步骤,我们就完成了监听器的编写和注册,Web服务器在启动时,就会自动把在web.xml中配置的监听器注册到ServletContext对象上,这样开发好的MyServletContextListener监听器就可以对ServletContext对象进行监听了
监听HttpSession域对象的创建和销毁
HttpSessionListener 接口用于监听HttpSession对象的创建和销毁
package m.xk.listener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** 作者:吴志龙 日期:2018年8月23日 **/ public class SessionListener implements HttpSessionListener{ //session对象的创建 @Override public void sessionCreated(HttpSessionEvent arg0) { // TODO Auto-generated method stub System.out.println( arg0.getSession().getId() + "创建了!!"); } //session对象的销毁 @Override public void sessionDestroyed(HttpSessionEvent arg0) { // TODO Auto-generated method stub System.out.println( arg0.getSession().getId() + "销毁了!!"); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>WebDemo1</display-name> <!-- 注册针对ServletContext对象进行监听的监听器 --> <listener> <!--监听器的描述 --> <description>application对象监听器</description> <!--监听器类 全类名 --> <listener-class>m.xk.listener.ApplicationListener</listener-class> </listener> <!-- 注册针对HttpSession对象进行监听的监听器 --> <listener> <description>session监听器</description> <listener-class>m.xk.listener.SessionListener</listener-class> </listener> <!-- 配置HttpSession对象的销毁时机 --> <session-config> <!--配置HttpSession对象的1分钟之后销毁 --> <session-timeout>1</session-timeout> </session-config> <!--注册针对ServletRequest对象进行监听的监听器--> <listener> <description>request监听器</description> <listener-class>m.xk.listener.RequestListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
当我们访问jsp页面时,HttpSession对象就会创建,此时就可以在HttpSessionListener观察到HttpSession对象的创建过程了,我们可以写一个jsp页面观察HttpSession对象创建的过程。
监听ServletRequest域对象的创建和销毁
ServletRequestListener接口用于监听ServletRequest 对象的创建和销毁
ServletRequest域对象创建和销毁时机:
创建:用户每一次访问都会创建request对象
销毁:当前访问结束,request对象就会销毁
用户每一次访问都会创建request对象,当访问结束后,request对象就会销毁。
package m.xk.listener; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; /** * 作者:吴志龙 日期:2018年8月23日 * **/ public class RequestListener implements ServletRequestListener { // request对象销毁 @Override public void requestDestroyed(ServletRequestEvent arg0) { // TODO Auto-generated method stub System.out.println(arg0.getServletRequest() + "销毁了!!"); } // request对象创建 @Override public void requestInitialized(ServletRequestEvent arg0) { // TODO Auto-generated method stub System.out.println(arg0.getServletRequest() + "创建了!!"); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>WebDemo1</display-name> <!-- 注册针对ServletContext对象进行监听的监听器 --> <listener> <!--监听器的描述 --> <description>application对象监听器</description> <!--监听器类 全类名 --> <listener-class>m.xk.listener.ApplicationListener</listener-class> </listener> <!-- 注册针对HttpSession对象进行监听的监听器 --> <listener> <description>session监听器</description> <listener-class>m.xk.listener.SessionListener</listener-class> </listener> <!-- 配置HttpSession对象的销毁时机 --> <session-config> <!--配置HttpSession对象的1分钟之后销毁 --> <session-timeout>1</session-timeout> </session-config> <!--注册针对ServletRequest对象进行监听的监听器--> <listener> <description>request监听器</description> <listener-class>m.xk.listener.RequestListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
监听域对象中属性的变更的监听器
域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器。
这三个监听器接口分别是ServletContextAttributeListener, HttpSessionAttributeListener 和ServletRequestAttributeListener,这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>WebDemo1</display-name> <!-- 注册针对ServletContext对象进行监听的监听器 --> <listener> <!--监听器的描述 --> <description>application对象监听器</description> <!--监听器类 全类名 --> <listener-class>m.xk.listener.ApplicationListener</listener-class> </listener> <!-- 注册针对HttpSession对象进行监听的监听器 --> <listener> <description>session监听器</description> <listener-class>m.xk.listener.SessionListener</listener-class> </listener> <!--注册针对ServletRequest对象进行监听的监听器--> <listener> <description>request监听器</description> <listener-class>m.xk.listener.RequestListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
package m.xk.listener; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** 作者:吴志龙 日期:2018年8月23日 **/ public class ApplicationListener implements ServletContextAttributeListener{ //application对象添加属性值 @Override public void attributeAdded(ServletContextAttributeEvent arg0) { // TODO Auto-generated method stub System.out.println("添加属性:"+arg0.getName()+"----"+arg0.getValue()); } //application对象删除属性 @Override public void attributeRemoved(ServletContextAttributeEvent arg0) { // TODO Auto-generated method stub System.out.println("删除属性:"+arg0.getName()+"----"+arg0.getValue()); } //application对象替换属性 @Override public void attributeReplaced(ServletContextAttributeEvent arg0) { // TODO Auto-generated method stub System.out.println("替换属性:"+arg0.getName()+"----"+arg0.getValue()); } }
package m.xk.listener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** 作者:吴志龙 日期:2018年8月23日 **/ public class SessionListener implements HttpSessionAttributeListener{ //session对象添加属性 @Override public void attributeAdded(HttpSessionBindingEvent arg0) { // TODO Auto-generated method stub System.out.println("添加属性:"+arg0.getName()+"----"+arg0.getValue()); } //session对象删除属性 @Override public void attributeRemoved(HttpSessionBindingEvent arg0) { // TODO Auto-generated method stub System.out.println("删除属性:"+arg0.getName()+"----"+arg0.getValue()); } //session对象替换属性 @Override public void attributeReplaced(HttpSessionBindingEvent arg0) { // TODO Auto-generated method stub System.out.println("替换:"+arg0.getName()+"----"+arg0.getValue()); } }
package m.xk.listener; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; /** * 作者:吴志龙 日期:2018年8月23日 * **/ public class RequestListener implements ServletRequestAttributeListener { //request对象添加属性 @Override public void attributeAdded(ServletRequestAttributeEvent arg0) { // TODO Auto-generated method stub System.out.println("添加属性:"+arg0.getName()+"----"+arg0.getValue()); } //request对象删除属性 @Override public void attributeRemoved(ServletRequestAttributeEvent arg0) { // TODO Auto-generated method stub System.out.println("删除属性:"+arg0.getName()+"----"+arg0.getValue()); } //request对象替换属性 @Override public void attributeReplaced(ServletRequestAttributeEvent arg0) { // TODO Auto-generated method stub System.out.println("替换属性:"+arg0.getName()+"----"+arg0.getValue()); } }
Session绑定的事件监听器
保存在Session域中的对象可以有多种状态:绑定(session.setAttribute("bean",Object))到Session中;从 Session域中解除(session.removeAttribute("bean"))绑定;随Session对象持久化到一个存储设备中;随Session对象从一个存储设备中恢复
Servlet 规范中定义了两个特殊的监听器接口"HttpSessionBindingListener和HttpSessionActivationListener"来帮助JavaBean 对象了解自己在Session域中的这些状态: ,实现这两个接口的类不需要 web.xml 文件中进行注册。
HttpSessionBindingListener接口
实现了HttpSessionBindingListener接口的JavaBean对象可以感知自己被绑定到Session中和 Session中删除的事件
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.xk.bean.*" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>session监听器页面操作</title> </head> <body> <% //将javabean对象绑定到Session中 session.setAttribute("bean",new Dept("龙之殇")); //从Session中删除javabean对象 session.removeAttribute("bean"); %> </body> </html>
package m.xk.listener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; /** * 作者:吴志龙 日期:2018年8月23日 * **/ public class SessionBuilder implements HttpSessionBindingListener { private String name; public SessionBuilder(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } //对象绑定事件 @Override public void valueBound(HttpSessionBindingEvent arg0) { // TODO Auto-generated method stub System.out.println(name+"被加到session中了"); } //对象解绑事件 @Override public void valueUnbound(HttpSessionBindingEvent arg0) { // TODO Auto-generated method stub System.out.println(name+"被session踢出来了"); } }
HttpSessionActivationListener接口
实现了HttpSessionActivationListener接口的JavaBean对象可以感知自己被活化(反序列化)和钝化(序列化)的事件
当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被钝化(序列化)之前,web服务器调用该javabean对象的void sessionWillPassivate(HttpSessionEvent event) 方法。这样javabean对象就可以知道自己将要和HttpSession对象一起被序列化(钝化)到硬盘中.
当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被活化(反序列化)之后,web服务器调用该javabean对象的void sessionDidActive(HttpSessionEvent event)方法。这样javabean对象就可以知道自己将要和 HttpSession对象一起被反序列化(活化)回到内存中
package m.xk.listener; import java.io.Serializable; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionEvent; /** 作者:吴志龙 日期:2018年8月23日 **/ public class SessionActivation implements HttpSessionActivationListener,Serializable { private String name; public SessionActivation(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void sessionDidActivate(HttpSessionEvent arg0) { // TODO Auto-generated method stub System.out.println(name+"和session一起从硬盘反序列化(活化)回到内存了,session的id是:"+arg0.getSession().getId()); } @Override public void sessionWillPassivate(HttpSessionEvent arg0) { // TODO Auto-generated method stub System.out.println(name+"和session一起被序列化(钝化)到硬盘了,session的id是:"+arg0.getSession().getId()); } }
为了观察绑定到HttpSession对象中的javabean对象随HttpSession对象一起被钝化到硬盘上和从硬盘上重新活化回到内存中的的过程,我们需要借助tomcat服务器帮助我们完成HttpSession对象的钝化和活化过程
<?xml version="1.0" encoding="UTF-8"?> <Context> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"> <Store className="org.apache.catalina.session.FileStore" directory="gacl"/> </Manager> </Context>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.xk.bean.*" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>session监听器页面操作</title> </head> <body> <% //将javabean对象绑定到Session中 session.setAttribute("bean",new Dept("龙之殇")); %> </body> </html>
二、过滤器
Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。
Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
- 调用目标资源之前,让一段代码执行。
- 是否调用目标资源(即是否让用户访问web资源)。
- 调用目标资源之后,让一段代码执行。
web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对 象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方 法,即web资源就会被访问,否则web资源不会被访问。
编写Filter步骤:
1.编写java类实现Filter接口,并实现其doFilter方法
2.在 web.xml 文件中使用<filter>和<filter-mapping>元素对编写的filter类进行注册,并设置它所能拦截的资源。
package com.xk.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * 作者:吴志龙 日期:2018年8月24日 过滤器 **/ public class CharsetFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub System.out.println("----过滤器销毁----"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { // 对request和response进行一些预处理 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); System.out.println("过滤器执行前!!!"); // 让目标资源执行,放行 filterChain.doFilter(request, response); System.out.println("过滤器执行后!!!!"); } @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("----过滤器初始化----"); } }
注册过滤器
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>WebDemo1</display-name> <!--配置过滤器 --> <filter> <filter-name>charsetFilter</filter-name> <filter-class>com.xk.filter.CharsetFilter</filter-class> </filter> <!--映射过滤器--> <filter-mapping> <filter-name>charsetFilter</filter-name> <!--过滤的请求路径 /*代表过滤所有的请求--> <url-pattern>index7.jsp</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
过滤器链:
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
Filter的生命周期:
Filter的创建
Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作,filter对象只会创建一次,init方法也只会执行一次。通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
Filter过滤:
Filter初始化后一直处于等待过滤的状态:每次请求都会执行过滤行为,会传入一个FilterChain对象来进行对资源进行释放。
Filter的销毁
Web容器调用destroy方法销毁Filter。destroy方法在Filter的生命周期中仅执行一次。在destroy方法中,可以释放过滤器使用的资源
<dispatcher> 子元素可以设置的值及其意义:
- REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
- INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
- FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
- ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。