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>