5.1.1 概念
过滤器(filter)是能够对请求和响应的头属性(header)和能容体(body)进行操作的特殊Web构件。与一般Web构件(如servlet,JSP)不同的是,过滤器自身并不直接生成Web响应,若干个过滤器可以依次对一个Web资源的请求和响应进行作用。
过滤器取代了早先的Servlet链接的功能。更主要的是,过滤器是Servlet2.3版本之后的标准Web构件。它的使用方法比较方便,功能也相当强。如图5-1所示,过滤器可以在Web请求到达servlet(或JSP)之前和在servlet返回响应之后对二者进行操作。过滤器和servlet之间是多对多的关系,一个过滤器可以对多个servlet的请求和响应进行过滤(如Filter A),一个servlet也可以被多个过滤器作用(如servlet3被Filter B,C,D过滤器链过滤)。
过滤器的主要功能包括:
* 对Web请求进行分析,对输入数据进行预处理
* 阻止请求和响应的进行;
* 根据功能改动请求的头信息和数据体;
* 根据功能改动响应的头信息和数据体;
* 和其他Web资源协作。
* 对用户请求进行统一认证;
* 对用户发送的数据进行过滤或替换;
* 对用户的访问进行记录和审核;
读者可能会提出一个问题:这些功能在servlet中都可以实现。确实。前面讲到的servlet API 能够对Web请求和响应进行各种各样的操作,而过滤器的主要意义是提供了一种更方便高效的编程结构。对于许多servlet都需要的功能,使用过滤器独立实现会简化servlet,避免功能重复。另外,Web容器提供了方便的过滤器管理机制,可以让编程人员更轻松地编写Web程序。
可见过滤器程序的特点是通用性和可移植性。如果一个过滤器在程序中只能用在一个servlet或JSP上面而不能被其他Web程序借用,这个过滤器的存在就没有太大意义。通常的过滤器可以用在多种情况下,比如安全保护、运行记录、图像转化、数据压缩、加密解码以及XML转化等。
5.1.2 过滤器的使用
Servlet技术中有关过滤器的API包括javax.servlet包中的Filter,FilterChain和FilterConfig接口。
过滤器要实现javax.servlet.Filter接口。与servlet相似,Filter接口中有init(),destroy()方法。Init方法在初始化时做准备工作,destroy方法在它被Web容器清除之前完成收尾工作,主要的过滤功能在doFilter方法中实现。
程序5.1是javax.servlet.Filter接口的源代码:
package javax.servlet; import java.io.IOException; public interface Filter { public void init(FilterConfig filterConfig) throws ServletException; public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException; public void destroy(); }
程序 5.1
5.1.3 如何使用过滤器实现职责链模式
5.1.4 如何使用过滤器实现装饰器(decrator)设计模式
5.1.5 用MyEclipse开发过滤器使用的例子
登陆验证:
package org.sky.darkness.filter ; import java.io.* ; import javax.servlet.* ; import javax.servlet.http.* ; public class LoginFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException{} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Session属于HTTP范畴,所以ServletRequest对象需要先转换成HttpServletRequest对象 HttpServletRequest req = (HttpServletRequest)request ; HttpSession session = req.getSession() ; // 如果session不为空,则可以浏览其他页面 if(session.getAttribute("uname")!=null) { chain.doFilter(request,response) ; } else { // 通过requestDispatcher跳转到登陆页 request.getRequestDispatcher("login.jsp").forward(request,response) ; } } public void destroy() {} }; /* <filter> <filter-name>login</filter-name> <filter-class> org.sky.darkness.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>login</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Servlet程序的主要分类?
标准Servlet(JSP)-MVC
过滤Servlet(过滤器)
监听Servlet(监听器)
过滤器在WEB中主要起什么作用:
1. 过滤器是程序运行之后加入的
2. 功能:
任何网站都需要对用户是否登陆进行过滤
网上聊天系统,屏蔽非法文字
对请求内容进行统一编码
写一个Filter类都必须继承(implements) Filter接口
public void init(FilterConfig filterConfig)
throws ServletException
过滤器初始化是在容器启动时自动初始化的
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException public void destroy()
package org.sky.darkness.filter ; import java.io.* ; import javax.servlet.* ; public class FirstFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println("** 过滤器初始化...") ; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("** 过滤器 doFilter (chain之前)...") ; chain.doFilter(request,response) ; System.out.println("** 过滤器 doFilter (chain之后)...") ; } public void destroy() { System.out.println("** 过滤器销毁...") ; } }; /* <filter> <filter-name>first</filter-name> <filter-class> org.sky.darkness.filter.FirstFilter</filter-class> </filter> <filter-mapping> <filter-name>first</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> */
xml文件中的配置:
<filter> <filter-name> first </filter-name> <filter-class> org.sky.darkness.filter.FirstFilter</filter-class> </filter> <filter-mapping> <filter-name> first </filter-name> <url-pattern>/*</url-pattern>//与servlet的不一样,此处表示对哪个页面进行过滤 </filter-mapping>
如果过滤器要将内容传递到目的地,则需要FilterChain,将请求继续向下转发chain.doFilter(request, response)
过滤器执行两次,chain之前执行一次,chain之后执行一次
1.过滤非法文字:
string content = request.getParameter(“content”); if(content != null){ if(content.indexOf(“AAA”)==-1)chain.doFilter(req,res); else out .println(“有非法文字!!!”); }else chain.doFilter(req,res);
package org.sky.darkness.filter ; import java.io.* ; import javax.servlet.* ; public class CharFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { // System.out.println("** 过滤器初始化...") ; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String content = request.getParameter("content") ; // 如果indexOf返回-1则表示没有查到所要的内容 if(content!=null) { if(content.indexOf("AAA")==-1) { chain.doFilter(request,response) ; } else { System.out.println("有非法文字") ; // 如果需要的话,此处依然可以使用RequestDispatcher进行跳转 } } else { chain.doFilter(request,response) ; } } public void destroy() { // System.out.println("** 过滤器销毁...") ; } }; /* <filter> <filter-name>char</filter-name> <filter-class>org.sky.darkness.filter.CharFilter</filter-class> </filter> <filter-mapping> <filter-name>char</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> */
2. 进行统一编码
request.setCharacterEncoding(“gb2312”)
package org.sky.darkness.filter ; import java.io.* ; import javax.servlet.* ; public class EncodingFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { // System.out.println("** 过滤器初始化...") ; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { request.setCharacterEncoding("GB2312") ; } catch (Exception e) { } chain.doFilter(request,response) ; } public void destroy() { // System.out.println("** 过滤器销毁...") ; } }; /* <filter> <filter-name>encoding</filter-name> <filter-class> org.sky.darkness.filter.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> */
3.登陆验证
Session属于HTTP范畴,所以ServletRequset对象需要先转换为 HttpServletRequest对象
HttpServletRequest req = (HttpServletRequest)req; HttpSession session = req.getSession(); if(session.getAttribute(“uname”) != null) chain.doFilter(req,res); else request.getRequestDispatcher(“login.jsp”).forward(req,res);
package org.sky.darkness.filter ; import java.io.* ; import javax.servlet.* ; import javax.servlet.http.* ; public class LoginFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { // System.out.println("** 过滤器初始化...") ; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Session属于HTTP范畴,所以ServletRequest对象需要先转换成HttpServletRequest对象 HttpServletRequest req = (HttpServletRequest)request ; HttpSession session = req.getSession() ; // 如果session不为空,则可以浏览其他页面 if(session.getAttribute("uname")!=null) { chain.doFilter(request,response) ; } else { // 通过requestDispatcher跳转到登陆页 request.getRequestDispatcher("login.jsp").forward(request,response) ; } } public void destroy() { // System.out.println("** 过滤器销毁...") ; } }; /* <filter> <filter-name>login</filter-name> <filter-class> org.sky.darkness.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>login</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> */
原则:开发时只专注于具体的业务实现,而对于登陆之类的验证,肯定属于组件,向整个程序中单独加入的。
监听器
5.2.1概念
5.2.2 事件监听器的使用
5.2.3 MyEclipse开发事件监听器使用的例子
监听器是指对于整个WEB环境的监听
主要有以下三类:
ServletContext:Servlet上下文
Session:对Session监听
Request监听
一、 对ServletContext的监听
在web端实现监听 = 实现一系列的监听接口
1、 ServletContextListener:对整个 servlet上下文进行监听(启动,销毁)
public void contextInitialized(ServletContextEvent sce):上下文初始化
public void contextDestroyed(ServletContextEvent sce):上下文销毁
ServletContextEvent事件:取得一个ServletContext (Applicaton)对象
public ServletContext getServletContext()
2、 ServletContextAttributeListener:对Servlet上下文属性进行监听
public void attributeAdded(ServletContextAttributeEvent scab):增加属性(setAttribute)
public void attributeRemoved(ServletContextAttributeEvent scab) :属性删除(removeAttribute)
public void attributeReplaced(ServletContextAttributeEvent scab):属性替换(第二次设置同一个属性)
ServletContextAttributeEvent事件:
public java.lang.String getName()
public java.lang.Object getValue()
回顾: 设置属性的方法
Public void setAttribute (String name ,Object value)
package org.sky.darkness.listener ; import javax.servlet.* ; public class ServletContextDemo implements ServletContextListener,ServletContextAttributeListener { private ServletContext application = null ; // 实现方法 public void contextInitialized(ServletContextEvent sce) { this.application = sce.getServletContext() ; System.out.println("** 上下文初始化 ...") ; System.out.println("** 当前虚拟目录的绝对路径:"+this.application.getRealPath("/")) ; } public void contextDestroyed(ServletContextEvent sce) { System.out.println("** 上下文销毁 ...") ; } public void attributeAdded(ServletContextAttributeEvent scab) { System.out.println("** 增加属性:"+scab.getName()+" --> "+scab.getValue()) ; } public void attributeRemoved(ServletContextAttributeEvent scab) { System.out.println("** 删除属性:"+scab.getName()+" --> "+scab.getValue()) ; } public void attributeReplaced(ServletContextAttributeEvent scab) { System.out.println("** 替换属性:"+scab.getName()+" --> "+scab.getValue()) ; } }; /* <listener> <listener-class> org.sky.darkness.listener.ServletContextDemo</listener-class> </listener> */
<!--测试页面-->
<% // getServletContext().setAttribute("name","LiXingHua") ; getServletContext().removeAttribute("name") ; %>
上下文监听主要是针对容器的:初始化、销毁、属性操作
二、 对Session监听
对session的创建、销毁、属性操作
Session属于http协议下的内容:javax.servlet.http.HttpSessionXxxx
1、 HttpSessionListener:对session的整体状况的监听
public void sessionCreated(HttpSessionEvent se)
public void sessionDestroyed(HttpSessionEvent se):session销毁
HttpSessionEvent事件:
public HttpSession getSession():取得当前操作的 session
2、 HttpSessionAttributeListener
public void attributeAdded(HttpSessionBindingEvent se)
public void attributeRemoved(HttpSessionBindingEvent se)
public void attributeReplaced(HttpSessionBindingEvent se)
HttpSessionBindingEvent事件:
public HttpSession getSession():取得当前的Session
public java.lang.String getName():取得属性的名称
public java.lang.Object getValue()
package org.sky.darkness.listener ; import javax.servlet.http.* ; public class HttpSessionDemo implements HttpSessionListener,HttpSessionAttributeListener { private HttpSession session ; // 实现方法 public void sessionCreated(HttpSessionEvent se) { this.session = se.getSession() ; System.out.println("** Session 创建 ....") ; System.out.println("** SessionID --> "+this.session.getId()) ; } public void sessionDestroyed(HttpSessionEvent se) { System.out.println("** Session 销毁 ....") ; } public void attributeAdded(HttpSessionBindingEvent se) { System.out.println("** Session 增加属性:"+se.getName()+" --> "+se.getValue()) ; } public void attributeRemoved(HttpSessionBindingEvent se) { System.out.println("** Session 删除属性:"+se.getName()+" --> "+se.getValue()) ; } public void attributeReplaced(HttpSessionBindingEvent se) { System.out.println("** Session 替换属性:"+se.getName()+" --> "+se.getValue()) ; } }; /* <listener> <listener-class> org.sky.darkness.listener.HttpSessionDemo</listener-class> </listener> */ <% // session.setAttribute("name","LXh") ; // session.removeAttribute("name") ; session.invalidate() ; %>
Session如何销毁?
1、 session超时
需要在xml文件中进行配置
<session-config>
<session-timetout>1</session-timeout>//session一分钟失效
</session-config>
2、 手工使session失效
Invalidate()
案例:
统计在线人员列表
实现那几个接口?
1、 在线人员列表是对所有人都起作用,所有的数据必须保存在application之中,这意味着在OnlineDemo中必须有一个ServletContext对象
2、 是针对session的变化进行的操作
如果登陆成功,则将用户名保存在session中
3、 如果用户注销,则将相应的用户名删除掉
因为用户名是多个,所以无法确定个数,使用list
package org.sky.darkness.listener ; import java.util.* ; import javax.servlet.* ; import javax.servlet.http.* ; public class OnLineDemo implements ServletContextListener,HttpSessionListener,HttpSessionAttributeListener { // 声明一个ServletContext对象 private ServletContext application = null ; public void contextInitialized(ServletContextEvent sce) { // 容器初始化时,向application中存放一个空的容器 this.application = sce.getServletContext() ; this.application.setAttribute("alluser",new ArrayList()) ; } public void contextDestroyed(ServletContextEvent sce) {} public void sessionCreated(HttpSessionEvent se) {} public void sessionDestroyed(HttpSessionEvent se) { // 将用户名称从列表中删除 List l = (List)this.application.getAttribute("alluser") ; String value = (String)se.getSession().getAttribute("uname") ; l.remove(value) ; this.application.setAttribute("alluser",l) ; } public void attributeAdded(HttpSessionBindingEvent se) { // 如果登陆成功,则将用户名保存在列表之中 List l = (List)this.application.getAttribute("alluser") ; l.add(se.getValue()) ; this.application.setAttribute("alluser",l) ; } public void attributeRemoved(HttpSessionBindingEvent se) {} public void attributeReplaced(HttpSessionBindingEvent se) {} }; /* <listener> <listener-class>cn.mldn.lxh.listener.OnLineDemo</listener-class> </listener> */
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.util.*"%> <form action="online.jsp" method="post"> 用户名: <input type="text" name="name"> <input type="submit" value="登陆"> <a href="logout.jsp">注销</a> </form> <!-- 向session接收输入的用户名 --> <% if(request.getParameter("name")!=null) { session.setAttribute("uname",request.getParameter("name")) ; } %> <h2>在线人员</h2> <hr> <% List l = (List)application.getAttribute("alluser") ; Iterator iter = l.iterator() ; while(iter.hasNext()) { %> <li><%=iter.next()%> <% } %> -------------------------------------------------------logout.jsp------------------- <% session.invalidate() ; %>