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-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>
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>
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>