J2EE梳理(十)——过滤器与监听器


来源于how2j

Filter过滤器

概念

  • WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
  • Filter就像一个一个哨卡,用户的请求需要经过Filter
    并且可以有多个过滤器
    在这里插入图片描述

Filter是如何实现拦截的?

  • Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
调用目标资源之前,让一段代码执行。
是否调用目标资源(即是否让用户访问web资源)。
调用目标资源之后,让一段代码执行。
  • web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对 象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方 法,即web资源就会被访问,否则web资源不会被访问。

写一个Filter

  • 开发一个简单的FirstFilter,用来打印用户访问ip地址和访问的页面:
  • 代码:
package filter;
 
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class FirstFilter implements Filter {
 
    @Override
    public void destroy() {
 
    }
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
 
        String ip = request.getRemoteAddr();
        String url = request.getRequestURL().toString();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = new Date();
        String date = sdf.format(d);
 
        System.out.printf("%s %s 访问了 %s%n", date, ip, url);
        chain.doFilter(request, response);
    }
 
    @Override
    public void init(FilterConfig arg0) throws ServletException {
 
    }
 
}
  • 解释:
    • HttpServletRequest request = (HttpServletRequest) req;doFilter()方法中的req参数的类型是ServletRequest,需要转换为HttpServletRequest类型方便调用某些方法;
    • chain.doFilter(request, response);:过滤器放行,表示继续运行下一个过滤器,或者最终访问的某个servlet,jsp,html等等
  • 效果:

在这里插入图片描述

  • web.xml配置:
<filter>
	    <filter-name>FirstFilter</filter-name>
	    <filter-class>filter.FirstFilter</filter-class>
	</filter>
 
	<filter-mapping>
	    <filter-name>FirstFilter</filter-name>
	    <url-pattern>/*</url-pattern>
	</filter-mapping>
  • 解释:
    • <url-pattern>/*</url-pattern>表示所有的访问都会过滤
    • 如果配置成<url-pattern>*.jsp</url-pattern>;就表示只过滤jsp
  • init()方法 :
    • 与Servlet需要配置自启动才会随着tomcat的启动而执行init()方法不一样。Filter一定会随着tomcat的启动自启动。
  • FIlter启动失败 :Filter是web应用非常重要的一个环节,如果Filter启动失败,或者本身有编译错误,不仅这个Filter不能使用,整个web应用会启动失败,导致用户无法访问页面
    在启动tomcat过程中,也会看到这样的字样:严重: Context [] startup failed due to previous errors

Filter配置登录验证

  • 我们可以通过在HeroListServlet中增加对session的判断代码来做到登陆验证。
  • 但是按照这样的做法,所有的Servlet都要加上一样的代码,就会显得比较累赘。
  • 与通过Filter处理中文问题一样,也可以通过Filter一次性解决所有的登陆验证问题
  • 代码:
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class AuthFilter implements Filter {
 
    @Override
    public void destroy() {
 
    }
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
 
        String uri = request.getRequestURI();
        if (uri.endsWith("login.html") || uri.endsWith("login")) {
            chain.doFilter(request, response);
            return;
        }
 
        String userName = (String) request.getSession().getAttribute("userName");
        if (null == userName) {
            response.sendRedirect("login.html");
            return;
        }
 
        chain.doFilter(request, response);
    }
 
    @Override
    public void init(FilterConfig arg0) throws ServletException {
 
    }
 
}
 
  • 解释:
    • 首先判断是否是访问的login.html和loginHero,因为这两个页面就是在还没有登陆之前就需要访问的
    • 代码:
    tring uri = request.getRequestURI();
            if (uri.endsWith("login.html") || uri.endsWith("login")) {
                chain.doFilter(request, response);
                return;
            }
    
    • 从Session中获取userName,如果没有,就表示不曾登陆过,跳转到登陆页面。
    • 代码:
    String userName = (String) request.getSession().getAttribute("userName");
    if (null == userName) {
            response.sendRedirect("login.html");
    	return;
    }
    
  • web.xml配置:
<filter>
    <filter-name>AuthFilter</filter-name>
    <filter-class>filter.AuthFilter</filter-class>
</filter>
 
<filter-mapping>
    <filter-name>AuthFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Listener监听器

LIstener教程

概念

  • 所谓的监听器是指对整个WEB环境的监听,当被监视的对象发生改变时,立即调用相应的方法进行处理
  • 监听器最常见的应用场景:
    • Java SE GUI编程,如之前学习java swing时配置各种监听器
    • Android 手机开发编程
  • JAVA WEB监听器:在JavaWeb的Listener也是这么个原理,但是它监听的内容不同,它可以监听Application、Session、Request对象,当这些对象发生变化就会调用对应的监听方法。

监听Context

对Context的监听分生命周期的监听,和Context上Attribute变化的监听两种。

  • 生命周期监听:ContextListener
  • 代码:
package listener;
 
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
 
public class ContextListener implements ServletContextListener {
 
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        System.out.println("web 应用销毁  ");
    }
 
    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        System.out.println("web 应用初始化 ");
 
    }
}
  • 解释:
    • ContextListener 实现接口ServletContextListener
      有两个方法:
    • public void contextDestroyed(ServletContextEvent arg0)对应当前web应用的销毁
    • public void contextInitialized(ServletContextEvent arg0)对应当前web应用的初始化
  • 配置web.xml
<listener>
    <listener-class>listener.ContextListener</listener-class>
</listener>
  • 效果:web初始化监听在这里插入图片描述
  • servlet发生变化:在这里插入图片描述
  • 其他类发生变化:也监听到并做出反应在这里插入图片描述
  • 不过我的修改是:在这里插入图片描述
  • 说明销毁调用的是之前的监听方法

监听ContextAttribute

  • ContextAttributeListener 实现接口ServletContextAttributeListener,
    分别提供如下方法:
//监听属性的增加
public void attributeAdded(ServletContextAttributeEvent e)
//监听属性的移除
public void attributeRemoved(ServletContextAttributeEvent e)
//监听属性的替换 
public void attributeReplaced(ServletContextAttributeEvent e)
  • 代码:
package listener;
 
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
 
public class ContextAttributeListener implements ServletContextAttributeListener {
 
    @Override
    public void attributeAdded(ServletContextAttributeEvent e) {
        System.out.println("增加属性 ");
        System.out.println("属性是" + e.getName());
        System.out.println("值是" + e.getValue());
 
    }
 
    @Override
    public void attributeRemoved(ServletContextAttributeEvent e) {
        // TODO Auto-generated method stub
        System.out.println("移除属性 ");
    }
 
    @Override
    public void attributeReplaced(ServletContextAttributeEvent e) {
        // TODO Auto-generated method stub
        System.out.println("替换属性");
    }
 
}
  • web.xml:
<listener>
    <listener-class>listener.ContextAttributeListener</listener-class>
</listener>
  • 用JSPtest:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%
    application.setAttribute("test", 1);
    application.setAttribute("test", 2);
    application.removeAttribute("test");
%>
  • 效果:
    在这里插入图片描述

监听Request

对Request的监听分生命周期的监听,和Request上Attribute变化的监听两部分。

  • RequestListner:
//当新创建了一个Request的时候触发,只要访问了服务端的资源,
就会创建一个Request,无论是jsp,servlet还是html
requestInitialized()
//当本次请求结束的时候触发
requestDestroyed()
//当有新增属性时触发
attributeAdded()
//当有替换属性时触发
attributeReplaced()
//当有移除属性时触发 
attributeRemoved()
  • 代码:
package listener;
 
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
 
public class RequestListener implements ServletRequestListener, ServletRequestAttributeListener {
 
    @Override
    public void requestDestroyed(ServletRequestEvent arg0) {
        System.out.println("销毁了一个Request ");
    }
 
    @Override
    public void requestInitialized(ServletRequestEvent arg0) {
        System.out.println("创建了一个Request ");
    }
 
    @Override
    public void attributeAdded(ServletRequestAttributeEvent e) {
        System.out.println("request 增加属性 ");
        System.out.println("属性是" + e.getName());
        System.out.println("值是" + e.getValue());
    }
 
    @Override
    public void attributeRemoved(ServletRequestAttributeEvent arg0) {
        System.out.println("request 移除属性 ");
         
    }
 
    @Override
    public void attributeReplaced(ServletRequestAttributeEvent arg0) {
        System.out.println("request 替换属性 ");
         
    }
}
  • web.xml
<listener>
    <listener-class>listener.RequestListener</listener-class>
</listener>  
  • 效果:在这里插入图片描述

利用Session监听器统计在线人数

  • HTTP协议是短链接的,所以无法在服务端根据建立了多少连接来统计当前有多少人在线。 不过可以通过统计session有多少来估计在线人数。

  • 一旦一个用户访问服务器,就会创建一个session. 如果该用户持续访问,那么该session会持续有效。

  • 如果经历了30分钟,该用户也没有做任何操作,就表示该用户“下线” 了,其对应的session也会被销毁。

  • 所以可以通过统计有多少session被保留来估计当前在线人数。

  • 编写 OnlineNumberListener:

    • OnlineNumberListener 实现接口HttpSessionListener

    • 当创建一个session的时候,就把数字+1,并且放在application里。

    • 这就是jsp里的application:ServletContext application = e.getSession().getServletContext();

    • 注: 第一次从application里取数据的时候,是空的,要设置为0。当销毁一个session的时候,把这个数字-1

  • 关于applicationContext什么的?

  • 代码:

package listener;
 
import javax.servlet.ServletContext;
 
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
 
public class OnlineNumberListener implements HttpSessionListener {
 
    @Override
    public void sessionCreated(HttpSessionEvent e) {
 
        ServletContext application = e.getSession().getServletContext();
 
        Integer online_number = (Integer) application.getAttribute("online_number");
 
        if (null == online_number)
            online_number = 0;
        online_number++;
        application.setAttribute("online_number", online_number);
         
        System.out.println("新增一位在线用户");
    }
 
    @Override
    public void sessionDestroyed(HttpSessionEvent e) {
 
        ServletContext application = e.getSession().getServletContext();
 
        Integer online_number = (Integer) application.getAttribute("online_number");
 
        if(null==online_number){
            online_number = 0;
        }
        else
            online_number--;
        application.setAttribute("online_number", online_number);
        System.out.println("一位用户离线");
    }
}
  • 配置web.xml
<listener>
    <listener-class>listener.OnlineNumberListener</listener-class>
</listener>  
  • checkOnlineNumber.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isELIgnored="false"%>
  
 当前 在线人数 : ${online_number}
  • 效果:在这里插入图片描述
    在这里插入图片描述

版权声明:本文为weixin_38719347原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。