过滤器及监听器
过滤器
介绍
Filter 即为过滤,用于在 Servlet 之外对 Request 或者 Response 进行修改。它主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行后处理。使用 Filter 的完整流程:Filter 对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再对服务器响应进行后处理。在一个 web 应用中,可以开发编写多个 Filter,这些 Filter 组合起来称之为一个 Filter 链。
单个过滤器
多个过滤器
若是一个过滤器链:先配置先执行(请求时的执行顺序);响应时:以相反的顺序执行。
在 HttpServletRequest 到达 Servlet 之前,拦截客户的 HttpServletRequest 。根据需要检查 HttpServletRequest,也可以修改 HttpServletRequest 头和数据。
在HttpServletResponse 到达客户端之前,拦截 HttpServletResponse。根据需要检查HttpServletResponse,也可以修改 HttpServletResponse 头和数据。
实现
我们可以通过实现一个叫做 javax.servlet.Fileter 的接口来实现一个过滤器,其中定义了三个方 法,init(), doFilter(), destroy() 分别在相应的时机执行。后期观察生命周期。Filter 的实现只需要两步:
Step1: 编写 java 类实现 Filter 接口,并实现其 doFilter 方法。
Step2: 在 web.xml 文件中对编写的 fifilter 类进行注册,并设置它所能拦截的资源。
public class Filter01 implements Filter {
@Override
public void destroy() {
System.out.println("Filter01 destroy...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Filter01...");
//请求到达资源之前处理的代码
System.out.println("Filter01请求到达资源之前处理的代码...");
//放行
chain.doFilter(request, response);
//响应客户端之前执行的代码
System.out.println("Filter01响应到客户端之前执行的代码...");
}
@Override
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("Filter01初始化了。。。");
}
}
<filter>
<display-name>Filter01</display-name>
<filter-name>Filter01</filter-name>
<filter-class>com.mage.filter.Filter01</filter-class>
</filter>
<filter-mapping>
<filter-name>Filter01</filter-name>
<url-pattern>/s01</url-pattern>
</filter-mapping>
Filter 接口中有一个 doFilter 方法,当开发人员编写好 Filter,并配置对哪个 web 资源进行拦截后,Web 服务器每次在调用 web 资源的 service 方法之前,都会先调用一下 fifilter 的 doFilter 方法。
因此可以达到如下效果:
调用目标资源之前,让一段代码执行。
是否调用目标资源(即是否让用户访问 web 资源)。
web 服务器在调用 doFilter 方法时,会传递一个 fifilterChain 对象进来,fifilterChain 对象是 fifilter接口中最重要的一个对象,它提供了一个 doFilter 方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则 web 服务器就会调用 web 资源的 service 方法,即 web 资源就会被访问,否则web 资源不会被访问。(本质是放行,调用 doFilter 方法后,即请求可以到达资源)
web.xml 配置各节点说明:
-
<filter>
指定一个过滤器。
<filter-name>
用于为过滤器指定一个名字,该元素的内容不能为空。<filter-class>
元素用于指定过滤器的完整的限定类名。<init-param>
元素用于为过滤器指定初始化参数,它的子元素<param-name>
指定参数的名字,<param-value>
指定参数的值。- 在过滤器中,可以使用
FilterConfig
接口对象来访问初始化参数。
-
<filter-mapping>
元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
-
<filter-name>
子元素用于设置filter的注册名称。该值必须是在<filter>
元素中声明过的过滤器的名字 -
<url-pattern>
设置 filter 所拦截的请求路径(过滤器关联的URL样式) -
<servlet-name>
指定过滤器所拦截的Servlet名称。 -
<dispatcher>
指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST
,INCLUDE
,FORWARD
和ERROR
之一,默认REQUEST
。用户可以设置多个<dispatcher>
子元素用来指定 Filter 对资源的多种调用方式进行拦截。
-
-
<dispatcher>
子元素可以设置的值及其意义
REQUEST
:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。INCLUDE
:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。FORWARD
:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。ERROR
:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
注意:Tomcat7 及以下的版本:上述解决 GET 乱码的方式不好,不能解决所有的问题,可以借助
HttpServletRequestWrapper 类和过滤器来完成转换乱码的问题:
①在过滤器中判断哪些需要解决
②定义 HttpServletRequestWrapper 的子类,并且重写 getParameter(String name) 方法,在该
方法中实现乱码的处理
③chain.doFilter(自定义类(HttpServletRequestWrapper 的子类),response)
监听器
介绍
web 监听器是一种 Servlet 中的特殊的类,它们能帮助开发者监听 web 中的特定事件,比如ServletContext,HttpSession,ServletRequest 的创建和销毁;变量的创建、销毁和修改等。可以在某些 动作前后增加处理,实现监控。例如可以用来统计在线人数等。
原理
1、存在事件源
2、提供监听器
3、为事件源注册监听器
4、操作事件源,产生事件对象,将事件对象传递给监听器,并且执行监听器相应监听方法
实现
监听器有三类8种:
⑴监听生命周期(三个域对象的创建和销毁):实现接口 ServletRequestListener、 HttpSessionListener 、ServletContextListener
⑵监听值的变化(属性添加,移除,替换):实现接口 ServletRequestAttributeListener、 HttpSessionAttributeListener、ServletContextAttributeListener
⑶针对 session 中的对象:监听 session 中的 java 对象(javaBean),javaBean 直接实现监听器的接口 HttpSessionBindingListener、HttpSessionActivationListener。
假设我们想做一个对在线人数的监控。
Step1:创建一个监听器,需要实现某种接口,根据需求选取 HttpSessionListener
Step2:在 web.xml 中配置该监听器
创建一个类,并实现 HttpSessionListener 接口,用来检测 Session 的创建和销毁。在类中定义一 个成员变量用来存储当前的 session 个数。
package com.mage.listener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* 监听器 由服务器管理
* 1、新建普通java类
* 2、实现相应的接口
* 3、重写接口
* 4、配置web.xml
*
*
*/
public class Listener01 implements HttpSessionListener{
int onlinePeople=0;
/**
* 监听SESSION创建
*/
@Override
public void sessionCreated(HttpSessionEvent se){
onlinePeople++;
//不同用户不同session,除非重新创建一个session才是最新的人数
se.getSession().getServletContext().setAttribute("onlinePeople", onlinePeople);
System.out.println("session被创建了。。。");
}
/**
* 监听SESSION销毁
* 正常关闭服务器不会销毁,因为会自动序列化到本地
*/
@Override
public void sessionDestroyed(HttpSessionEvent se){
onlinePeople--;
se.getSession().getServletContext().setAttribute("onlinePeople",onlinePeople);
System.out.println("session被销毁了。。。");
}
}
在 web.xml 中配置该监听器,让监听器生效
<listener>
<listener-class>com.mage.listener.Listener01</listener-class>
</listener>
做一个测试的 Servlet 用来访问,和显示当前在线人数
public class Servlet01 extends HttpServlet{
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException, IOException{
System.out.println("s01....");
//及时获取也是创建
int onlinePeople =(int) request.getSession().getServletContext().getAttribute("onlinePeople");
//输出
response.getOutputStream().write(("<h1>在线人数</h1>"+onlinePeople).getBytes("GBK"));
}
}