目录
Servlet过滤器就是服务器与客户端请求与响应的中间层组件,在实际项目开发中Servlet过滤器主要用于对浏览器的请求进行过滤处理,将过滤后的请求再转给下一个资源。
通过过滤器,可以实现例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。也可以例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
1.实现过滤器
在Servlet中,如果要定义一个过滤器,则直接让一个类实现javax.servlet.Filter接口即可,此接口定义了三个操作方法:
①public void init(FilterConfig filterConfig)
这是Servlet过滤器的初始化方法,Servlet容器创建Servlet过滤器实例后将调用这个方法。在这个方法中可以读取web.xml文件中Servlet过滤器的初始化参数。
FilterConfig接口的两个方法:(获取初始化参数)
String getInitParameter(java.lang.String name)
Enumeration getInitParameterNames()
String getFilterName():得到filter的名称
public ServletContext getServletContext():返回Servlet上下文对象的引用。
②public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
这个方法完成实际的过滤操作,当客户请求访问与过滤器关联的URL时,Servlet容器将先调用过滤器的doFilter方法。
FilterChain参数用于访问后续过滤器。在FilterChain接口中依然定义了一个同样的doFilter()方法,这是因为在一个过滤器后面可能存在着另外一个过滤器,也可能是请求的最终目标(Servlet),这样就通过FilterChain形成了一个“过滤链”的操作。
void doFilter(ServletRequest request, ServletResponse response)
③public void destroy()
Servlet容器在销毁过滤器实例前调用该方法,这个方法中可以释放Servlet过滤器占用的资源。
2.Filter实现拦截的原理
Filter接口中有一个doFilter方法,当开发人员编写好Filter类实现doFilter方法,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前(服务器内部对资源的访问机制决定的),都会先调用一下filter的doFilter方法。
3.filter的web.xml配置
在 web.xml 文件中使用<filter>和<filter-mapping>元素对编写的filter类进行注册,并设置它所能拦截的资源。
在web.xml中配置Servlet和Servlet过滤器,应该先声明过滤器元素,再声明Servlet元素。
两个或更多个过滤器应用到同一个资源,按照它们在配置文件中显示的先后次序调用它们。
其中:
<filter-mapping> : 将过滤器与 servlet 或 URL 模式相关联。
<filter-name> : 指定过滤器的名字,与<filter>中<filter-name>相对应;
<url-pattern> : 指定和过滤器关联的URL,为”/*”表示所有URL;
<dispatcher>,指定过滤器对应的请求方式,可以包含0-4个。可以是 REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST.
REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。
INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
<web-app>
<filter>
<filter-name>DemoFilter</filter-name>
<filter-class>xiaohua.DemoFilter</filter-class>
<init-param> <!-- 为过滤器实例提供初始化参数,可以有多个 -->
<param-name>param</param-name>
<param-value>value</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DemoFilter</filter-name>
<url-pattern>/*</url-pattern> <!-- 拦截所有URL -->
<dispatcher>REQUEST</dispatcher> <!-- 没有配置dispatcher就是默认request方式-->
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<filter>
<filter-name>FilterTwo</filter-name>
<filter-class>xiaohua.FilterTwo</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterTwo</filter-name>
<url-pattern>/xiaohua/*</url-pattern> <!-- 此容器只有在接收到对 /xiaohua目录中的资源的请求时才会应用该过滤器。 -->
</filter-mapping>
</web-app>
4.filter的生命周期
和Servlet一样Filter的创建和销毁也是由WEB服务器负责。init方法与destroy方法只会直接一次。
- 在应用启动的时候就进行装载Filter类
- 容器创建好Filter对象实例后,调用init()方法。
- 当用户访问的资源正好被Filter的url-pattern拦截时,容器会取出Filter类调用doFilter方法,下次或多次访问被拦截的资源时,Web容器会直接取出指定Filter对象实例调用doFilter方法
- 当应用服务被停止或重新装载了,则会执行Filter的destroy方法,Filter对象销毁。
例:
package xiaohua.HelloFilter;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 过滤器,测试
* @author xiaohua
*2018-11-13
*/
public class HelloFilter implements Filter{
// 创建实例
public HelloFilter(){
System.out.println("1. 创建过滤器实例");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("2. 执行过滤器初始化方法");
// 获取过滤器在web.xml中配置的初始化参数
String encoding = filterConfig.getInitParameter("encoding");
System.out.println(encoding);
// 获取过滤器在web.xml中配置的初始化参数 的名称
Enumeration<String> enums = filterConfig.getInitParameterNames();
while (enums.hasMoreElements()){
// 获取所有参数名称:encoding、path
String name = enums.nextElement();
// 获取名称对应的值
String value = filterConfig.getInitParameter(name);
System.out.println(name + "\t" + value);
}
}
// 过滤器业务处理方法: 在请求到达servlet之前先进入此方法处理公用的业务逻辑操作
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("3. 执行过滤器业务处理方法");
// 如果有下一个过滤器,进入下一个过滤器,否则就执行访问servlet
chain.doFilter(request, response);
System.out.println("5. Servlet处理完成,又回到过滤器");
}
@Override
public void destroy() {
System.out.println("6. 销毁过滤器实例");
}
}