菜鸟学java web(四)---------filter详解

来源:互联网 发布:毕业去向地图制作软件 编辑:程序博客网 时间:2024/06/11 01:58
这篇文章中,我们将学习过滤器技术。具体围绕filter的使用场景,如何创建filter,以及filter的使用示例三个方面展开。

一.什么是filter?

Servlet 过滤器是小型的 Web 组件,它们拦截请求和响应,以便查看、提取或以某种方式操作正在客户机和服务器之间交换的数据。过滤器是通常封装了一些功能的 Web 组件,这些功能虽然很重要,但是对于处理客户机请求或发送响应来说不是决定性的。典型的例子包括记录关于请求和响应的数据、处理安全协议、管理会话属性,等等。过滤器提供一种面向对象的模块化机制,用以将公共任务封装到可插入的组件中,这些组件通过一个配置文件来声明,并动态地处理。Servlet 过滤器中结合了许多元素,从而使得过滤器成为独特、强大和模块化的 Web 组件。也就是说,Servlet 过滤器是:

  • 声明式的:过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明。这样允许添加和删除过滤器,而无需改动任何应用程序代码或 JSP 页面。
  • 动态的:过滤器在运行时由 Servlet 容器调用来拦截和处理请求和响应。
  • 灵活的:过滤器在 Web 处理环境中的应用很广泛,涵盖诸如日志记录和安全等许多最公共的辅助任务。过滤器还是灵活的,因为它们可用于对来自客户机的直接调用执行预处理和后期处理,以及处理在防火墙之后的 Web 组件之间调度的请求。最后,可以将过滤器链接起来以提供必需的功能。
  • 模块化的:通过把应用程序处理逻辑封装到单个类文件中,过滤器从而定义了可容易地从请求/响应链中添加或删除的模块化单元。
  • 可移植的:与 Java 平台的其他许多方面一样,Servlet 过滤器是跨平台和跨容器可移植的,从而进一步支持了 Servler 过滤器的模块化和可重用本质。
  • 可重用的:归功于过滤器实现类的模块化设计,以及声明式的过滤器配置方式,过滤器可以容易地跨越不同的项目和应用程序使用。
  • 透明的:在请求/响应链中包括过滤器,这种设计是为了补充(而不是以任何方式替代)servlet 或 JSP 页面提供的核心处理。因而,过滤器可以根据需要添加或删除,而不会破坏 servlet 或 JSP 页面。

所以 Servlet 过滤器是通过一个配置文件来灵活声明的模块化可重用组件。过滤器动态地处理传入的请求和传出的响应,并且无需修改应用程序代码就可以透明地添加或删除它们。最后,过滤器独立于任何平台或者 Servlet 容器,从而允许将它们容易地部署到任何相容的 J2EE 环境中。

过滤器原理浅析:

Web 资源可以配置为没有过滤器与之关联(这是默认情况)、与单个过滤器关联(这是典型情况),甚至是与一个过滤器链相关联。那么过滤器究竟做什么呢? 像 servlet 一样,它接受请求并响应对象。然后过滤器会检查请求对象,并决定将该请求转发给链中的下一个组件,或者中止该请求并直接向客户机发回一个响应。如果请求被转发了,它将被传递给链中的下一个资源(另一个过滤器、servlet 或 JSP 页面)。在这个请求设法通过过滤器链并被服务器处理之后,一个响应将以相反的顺序通过该链发送回去。这样就给每个过滤器都提供了根据需要处理响应对象的机会。

二.为什么提供filter技术:
filter是一个可插的java 组件,我们可以使用filter在请求传递给servlet之前对其进行拦截,还可以在servlet处理之后拦截响应信息。
filter的一些常用使用使用场景如下:
1、打印请求参数到日志文件中。
2、对请求的资源进行认证处理(即权限控制)。
3、格式化请求头或者请求体(过滤敏感词,解决乱码)。
4、压缩响应信息。
5、在响应信息上加上cookie或者header等信息。
正如我之前说的,filter是可插拔的,它们被配置在web.xml文件中,我们可以随时更改web.xml文件以决定filter是否被使用。对同一个资源我们可以有多个filter。

三、filter接口简介:
filter接口中提供一系列filter生命周期方法,它被servlet容器所管理。
其方法如下:
void init(FilterConfig paramFilterConfig)
当容器初始化filter时,会调用此方法。这个方法在filter生命周期中仅被调用一次,我们应该在此方法中初始化一些资源。可以通过FilterConfig对象获取初始化参数(init parameter)或者是上下文对象(servletContext).
doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain)
当将一个filter应用到一个资源上时,每次调用资源时都会调用这个方法。这个方法中有一个请求对象和一个响应对象,还有一个FilterChain对象。使用request和response两个对象可以对请求和响应进行一些处理。FilterChain通常是用来调用下一个filter(如果有的话)或者是放行。(责任链模式)
void destroy()
当容器卸载filter实例,将调用此方法。这个方法可以用来关闭资源。这个方法在filter生命周期中仅被调用一次。

四.关于WebFilter注解
这个注解是servlet3.0中用来声明filter的,加上这个注解我们便不需在web.xml中配置filter了。
这个方法可以定义filter初始化参数,filter名,url pattern等。如果你的filter经常会改变,那么建议使用web.xml,因为那样不需要你编译filter。在文章最后的实例中我们使用了这个注解。
五、如何在web.xml中配置过滤器
声明:
<filter>  <filter-name>RequestLoggingFilter</filter-name> <!-- mandatory -->  <filter-class>cn.edu.chd.filter.RequestLoggingFilter</filter-class> <!--mandatory-->  <init-param> <!-- optional -->  <param-name>test</param-name>  <param-value>testValue</param-value>  </init-param></filter>
映射:
<filter-mapping>  <filter-name>RequestLoggingFilter</filter-name> <!-- mandatory -->  <url-pattern>/*</url-pattern>   <servlet-name>LoginServlet</servlet-name>  <dispatcher>REQUEST</dispatcher></filter-mapping>
这里需要注意一下顺序,必须先有filter节点再有filter-mapping节点。
filter通常只有在客户端有请求时才执行,但是有时我们希望执行RequestDispatcher时也执行filter,这时候我们便需要配置dispatcher节点,值可以为:REQUEST,FORWARD,INCLUDE,ERROR,ASYNC.默认是request。
六、实例
1.解决全站乱码问题:
package 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;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import javax.servlet.http.HttpServletResponse;/** * @author Rowand jj * *过滤乱码的Filter */public class EncodingFilter implements Filter{    @Override    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException    {        HttpServletRequest req = (HttpServletRequest) request;        HttpServletResponse resp = (HttpServletResponse) response;                req.setCharacterEncoding("utf-8");        resp.setCharacterEncoding("utf-8");        resp.setContentType("text/html;charset=utf-8");        chain.doFilter(new MyRequest(req), resp);    }    @Override    public void destroy()    {        // TODO Auto-generated method stub            }    @Override    public void init(FilterConfig filterConfig) throws ServletException    {        // TODO Auto-generated method stub            }        class MyRequest extends HttpServletRequestWrapper    {        private HttpServletRequest request = null;        public MyRequest(HttpServletRequest request)        {            super(request);            this.request = request;        }                /* 拦截getParameter方法,解决get请求乱码问题*/        @Override        public String getParameter(String name)        {            String value = this.request.getParameter(name);            try            {                if(this.request.getMethod().equalsIgnoreCase("get"))                {                    value = new String(value.getBytes("iso8859-1"),this.request.getCharacterEncoding());                }                return value;            } catch (Exception e)            {                throw new RuntimeException(e);            }        }            }}
web.xml配置:
 <filter>      <filter-name>EncodingFilter</filter-name>      <filter-class>cn.edu.chd.web.filter.EncodingFilter</filter-class>  </filter>    <filter-mapping>  <filter-name>EncodingFilter</filter-name>     <url-pattern>/*</url-pattern>  </filter-mapping>
以上方式有个缺点就是使用了硬编码,假如我们需要更换码表,则需要改动源码,很不方便。所以我们应该将编码格式放在web.xml的context-param中(使用了动态代理和上面介绍的注解技术):
package cn.edu.chd.filter;import java.io.IOException;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;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.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebFilter("/*")public class CharacterEncodingFilter implements Filter{    private String characterEncoding = "iso8859-1";//默认编码    public void destroy()    {    }    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException    {        final HttpServletRequest req = (HttpServletRequest) request;        HttpServletResponse resp = (HttpServletResponse) response;        //        设置post请求的编码        req.setCharacterEncoding(characterEncoding);        //        设置响应的编码        resp.setCharacterEncoding(characterEncoding);        resp.setContentType("text/html;charset="+characterEncoding);        //        解决get请求乱码        chain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(),req.getClass().getInterfaces(),new InvocationHandler()        {            @Override            public Object invoke(Object proxy, Method method, Object[] args)                    throws Throwable            {                if(!method.getName().equals("getParameter"))//拦截getParameter方法                {                    return method.invoke(req, args);                }                if(!req.getMethod().equalsIgnoreCase("get"))//拦截get请求                {                    return method.invoke(req, args);                }                String value = (String) method.invoke(req, args);                if(value == null)                    return null;                return new String(value.getBytes("iso8859-1"),characterEncoding);            }        }), resp);    }    public void init(FilterConfig fConfig) throws ServletException    {//        使用web.xml文件中配置的编码        characterEncoding = fConfig.getServletContext().getInitParameter("CharacterEncoding");    }}
web.xml文件:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5"     xmlns="http://java.sun.com/xml/ns/javaee"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">    <context-param>    <param-name>CharacterEncoding</param-name>    <param-value>utf-8</param-value></context-param>  <welcome-file-list>    <welcome-file>index.jsp</welcome-file>  </welcome-file-list></web-app>
2.解决全站压缩问题:
原理是在过滤器中增强response对象,将增强后的response对象返回给servlet,servlet操作的response对象其实是增强后的response,当调用这个response的getOutputStream/getWriter等写方法时,会将写出的数据全部存到byteArrayOutputStream的内部缓冲区中,而不是直接返回给浏览器。然后过滤器再将缓冲区中的数据压缩,统一打给浏览器,这样就做到了压缩数据的功能。
package cn.edu.chd.filter;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.zip.GZIPOutputStream;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class GzipFilter implements Filter{    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException    {        HttpServletRequest req = (HttpServletRequest) request;        final HttpServletResponse resp = (HttpServletResponse) response;        final ByteArrayOutputStream baos = new ByteArrayOutputStream();                OutputStreamWriter osw = new OutputStreamWriter(baos,"utf-8");//硬编码...        final PrintWriter pw = new PrintWriter(osw);//别忘了这里要设置编码//        拦截getWriter和getOutputStream方法        chain.doFilter(req, (ServletResponse) Proxy.newProxyInstance(GzipFilter.class.getClassLoader(),resp.getClass().getInterfaces(),new InvocationHandler()        {            public Object invoke(Object proxy, Method method, Object[] args)                    throws Throwable            {                if(method.getName().equals("getWriter"))                {                    return pw;                }else if(method.getName().equals("getOutputStream"))                {                    return new MyServletOutputStream(baos);                }else                {                    return method.invoke(resp, args);                }            }        }));                pw.close();        byte[] data = baos.toByteArray();//获取缓冲区中的数据        System.out.println("[压缩前]:"+data.length);        ByteArrayOutputStream bout = new ByteArrayOutputStream();        GZIPOutputStream gout = new GZIPOutputStream(bout);        gout.write(data);        gout.close();        data = bout.toByteArray();        System.out.println("[压缩后]:"+data.length);        resp.setHeader("Content-Encoding","gzip");        resp.setContentLength(data.length);        resp.getOutputStream().write(data);    }    public void init(FilterConfig filterConfig) throws ServletException    {    }        public void destroy()    {    }}class MyServletOutputStream extends ServletOutputStream{    ByteArrayOutputStream baos = null;    public MyServletOutputStream(ByteArrayOutputStream baos)    {        this.baos = baos;    }    @Override    public void write(int b) throws IOException    {        baos.write(b);    }}
web.xml文件:
<filter>      <filter-name>GzipFilter</filter-name>    <filter-class>cn.edu.chd.filter.GzipFilter</filter-class>  </filter>  <filter-mapping>      <filter-name>GzipFilter</filter-name>      <url-pattern>/*</url-pattern>  </filter-mapping>









3 0
原创粉丝点击