日常生活里,自来水都是经过一层层的过滤处理才达到食用标准的,每一层过滤都起到一种净化的作用,今天所讲Servlet过滤器与自来水被过滤的原理相似,
1 定义
Servlet过滤器和 Servlet
很类似,注意的是它有拦截客户端请求的用法,Servlet过滤器可以改变请求中的内容,满足实际开发的需要。
开发来讲,过滤器实质上是在Web应用服务器里面的一个Web组件,目的用在拦截客户端(浏览器)与目标资源的请求,并把这些请求进行一定过滤处理在发给目标资源:
看出,Web容器部署过滤器后,不仅客户端发送的请求会经过过滤器的处理,要不要返回呢?请求的回应信息也同样要经过过滤器。
如果一个Web应用中使用一个过滤器不能解决实际的业务需求,那可以部署多个过滤器对业务请求多次处理,这样做就组成一个过滤器链,Web容器在处理过滤器时,将被过滤器的先后顺序对请求进行处理:
Web窗口中部署过滤器链,那么请求会一次按照过滤器顺序进行处理,在第一个过滤器处理一请求后,会传递给第二个过滤器进行处理,依次类推,传递到最后一个过滤器为止,再将请求交给目标资源进行处理,目标资源在处理经过过滤的请求后,其回应信息再从最后一个过滤器依次传递到第一个过滤器,最后传送到客户端;
2 过滤器核心对象
过滤器对象放置在 javax.servlet
包中,叫做 Filter, 是个接口,与过滤器相关的对象还有 FilterConfig对象 与 FilterChain对象,这两个也同样是接口对象,位于 javax.servlet
包中,分别作为过滤器的配置对象与过滤器的传递工具。在实际开发,定义过滤器对象只需要直接或间接地实现 Filter接口即可。下图的MyFilter1过滤器与MyFilter2过滤器,而 FilterConfig对象 与 FilterChain对象 用在过滤器的相关操作:
Filter接口
每个过滤器对象只直接或间接地实现 Filter接口,在Filter接口中定义了3个方法,分别是 init(),doFilter()和destroy(),方法及说明如表:
方法 | 声明 |
---|---|
public void init(FilterConfig filterConfig) throws ServletException | 过滤器初始化方法,该方法在过滤器初始化时调用 |
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException | 对请求进行过滤处理 |
public void destroy() | 销毁方法,以便释放资源 |
FilterConfig接口
Servlet容器进行实现,主要是获取过滤器中的配置信息,其方明及说明如表:
方法 | 说明 |
---|---|
public String getFilterName() | 用于获取过滤器的名字 |
public ServletContext getServletContext() | 获取Servlet上下文 |
public String getInitParameter(String name) | 获取过滤器的初始化参数值 |
public Enumeration getInitParameterNames() | 获取过滤器的所有初始化参数 |
FilterChain接口
它由Servlet容器进行实现,在这个接口中一个方法,其方法声明:public void doFilter(ServletRequest request, ServletResponse response)throws IOException,ServletException
该方法将过滤的请求传给下一个过滤器,如果此过滤器已经是过滤器链中的最后一个过滤器,那么,请求传送给目标资源。
过滤器创建于配置
创建一个过滤器对象需要实现 javax.serjavax.servlet.Filter接口
,下面演示过滤器的创建:
创建名称为 MyFilter的过滤器对象,其代码如下:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servletFilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 过滤器
*/
public class MyFilter implements Filter{
//初始化方法
public void init(FilterConfig fConfig) throws ServletException{
//初始化处理
}
//过滤处理方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletEXception {
//过滤处理
chain.doFilter(request, response);
}
//销毁方法
public void destroy(){
//释放资源
}
}
过滤器的init()对过滤器的初始化进行处理,destroy()是过滤器的销毁方法,主要是释放资源,对于过滤器处理的业务需要编写到doFilter()里面,在请求过滤需调用chain参数的doFilter()将请求向下传给下一过滤器或目标资源:
使用过滤器并不一定要将请求向下传递到下一过滤器或目标资源,如果业务逻辑需要,也可以在过滤处理后,直接回应客户端;
过滤器与Servlet十分相似,在创建后同样对其配置,过滤器的配置主要分为两个步骤,分别在声明过滤器对象和创建过滤器映射,创建名为MyFilter的过滤器对象:
<!- 过滤器声明 ->
<filter>
<!- 过滤器的名称 ->
<filter-name>MyFilter</filter-name>
<!- 过滤器的完整类名 ->
<filter-class>com.lyq.Myilter</filter-class>
</filter>
<!- 过滤器映射 ->
<filter-mapping>
<!- 过滤器名称 ->
<filter-name>MyFilter</filter-name>
<!-- 过滤器 URL映射 ->
<url-pattern>/MyFilter</filter-name>
<!- 过滤器URL映射 ->
<url-pattern>/MyFilter</url-pattern>
</filter-mapping>
< filter>标签用在声明过滤器对象,这个标签中必须配置两个子元素,分别是过滤器的名称与过滤器完整类名,其中< filter-name>定义过滤器的名称,< filter-class>指定过滤器的完整类名。
< filter-mapping>标签用于创建过滤器的映射,它的主要作用是指定Web应用中,哪些URL应用哪一个过滤器进行处理,在< filter-mapping>标签里面需要你自己指定过滤器应用的URL。
< filter>标签中的< filter-name>可以是自定义的名称,而< filter-mapping>标签中的< filter-name>是指定已定义的过滤器名称,没法改变。它需要和< filter>标签中的< filter-name>一一对应;
我们试着创建一个过滤器,完成网站访问计数器的功能,在web.xml文件的配置里,将网站访问量的初始值设置为5000。
新建名称是CountFilter的类,该类实现 javax.servlet.Filter接口
,它是个过滤器对象,通过该过滤器实现统计网站访问人数,关键代码:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class CountFilter implements Filter{
//来访数量
private int count;
@Override
public void init(FilterConfig filterConfig) throws ServletException{
String param = filterConfig.getInitParameter("count");
count++;
//将ServletRequest 转换成 HttpServletRequest
HttpServletRequest req = (HttpServletRequest)request;
//获取ServletContext
ServletContext context = req.getSession.getServletContext();
context.setAttribute("count", count);
chain.doFilter(request, response);
}
@Override
public void destroy(){
}
}
在ComFiler类中,包含一个成员变量 count
,用于记录网站访问人数,该变量在过滤器的初始化方法init()
中被赋值,它的初始值通过FilterConfig对象读取配置文件中的初始化参数就可以得到;
计数器 count
变量的值在CountFilter类的 doFilter()
中递增, 因为客户端在请求服务器中的Web应用时侯,过滤器拦截请求通过 doFilter()方法进行过滤处理,所以当client请求Web应用时,计数器 ount
的值将加1, 为了能够访问计数器中的值,实例中我们把它放置在 Servlet
上下文中,Servlet
上下文对象通过将 ServletRequest 转换为 HttpServletRequest对象
后获取。
写过滤器对象需要我们实现javax.servlet.Filter接口
,实现该接口后需要对Filter对象
的3个方法进行实现,在这3个方法中,除了 doFilter()方法外,如果在业务逻辑中不涉及初始化方法 init()与销毁方法 destroy(), 可以不编写任何代码对其进行空实现,如实例中的 destroy()方法。
配置已创建的 CountFilter对象,此操作通过配置web.xml文件
进行实现。
<filter>
<filter-name>CountFilter</filter-name>
<filter-class>com.lyq.CountFilter</filter-class>
<init-param>
<param-name>count</param-name>
<param-value>5000</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CountFilter</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
CountFilter对象
的配置主要通过声明过滤器及创建过滤器的映射进行实现,其中声明过滤器要通过 < filter>标签进行实现。在声明过程中,实例用< init-param>标签配置过滤器的初始化参数,初始化参数的名称为count,参数值为5000;
如果直接对过滤器对象的成员变量进行赋值,那在过滤器被编译后将不可修改,所以,实例中将过滤器对象中的成员变量定义为过滤器的初始化参数,提高了代码的灵活性;
创建程序中的首页index.jsp
页面,在该页面中通过JSP内置对象Application获取计数器的值。关键代码如下:
<body>
<h2>
欢迎光临, <br>
你是本站的第【
<%=application.getAttribute("count") %>
】位访客!
</h2>
</body>
因为在web.xml中将计数器的初始值设置为 5000,所以实例运行后,计数器的数值变为大于5000的数,在多次刷新页面后,实例运行结果: