Java web基础总结七之—— Filter与Listener
一.Filter
1. Filter简介
Filter是Servlet体系中非常重要的一个技术。Filter的意思是过滤器,那么它过滤的是什么呢?就是web服务器管理的所有web资源。例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。它的代码逻辑执行在访问这些资源之前,从而进行一些特殊的操作。例如实现权限访问控制、编码和词汇的过滤等一些高级功能。
Servlet API中提供了一个Filter接口,我们一般编写一个Java类实现这个接口。可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。
2. Filter的实现原理
Filter和filterChain是一个典型的责任链设计模式。Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在访问这个web资源之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可以实现:调用目标资源之前,让一段代码执行;是否让用户访问web资源;调用目标web资源之后,让一段代码执行.
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个FilterChain。web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
3. Filter开发的步骤
首先,编写java类实现Filter接口,并实现其doFilter方法。例如下面的防止xss注入的filter。
XssFilter.java代码:
<span style="font-size:14px;">package com.cc;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class XssFilter implements Filter {
FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
}
}
</span>
XssHttpServletRequestWrapper.java代码:
<span style="font-size:14px;">package com.cc;
import org.springframework.web.util.HtmlUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = cleanXSS(values[i]);
}
return encodedValues;
}
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null)
return null;
return cleanXSS(value);
}
private String cleanXSS(String value) {
return HtmlUtils.htmlEscape(value);
}
}
</span>
然后,在 web.xml 文件中使用<filter>和<filter-mapping>元素对编写的filter类进行注册,并设置它所能拦截的资源。如:
<filter>
<filter-name>XssSqlFilter</filter-name>
<filter-class>com.cc.XssFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XssSqlFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
<filter-class>元素用于指定过滤器的完整的限定类名。
<init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
<filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字
<url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
<servlet-name>指定过滤器所拦截的Servlet名称。
<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher>子元素用来指定 Filter 对资源的多种调用方式进行拦截。<dispatcher> 子元素可以设置的值及其意义:
REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
4. Filter的生命周期。
Filter和Servlet一样,创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init(FilterConfig filterConfig)方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作。filter对象只会创建一次,init方法也只会执行一次
Filter的doFilter方法会在每一次请求相关的web资源时,都会被调用。
在Web容器卸载 Filter 对象之前,调用destroy()方法。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。
我们在配置filter时,可以使用<init-param>为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:
String getFilterName():得到filter的名称。
String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
ServletContext getServletContext():返回ServletContext的对象。
二.Listener
Listener的主要作用是: 做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。我们可以编写一个实现了javax.servlet.ServletContextListener 接口的java类,在这个类中实现响应的逻辑代码。它web应用启动时,Listener也会首先随之启动,接着进行初始化,只初始化一次,当web应用停止时,他也会被web容器销毁。
例如,spring的实现的入口就是一个Listener:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
三.servlet 、listener、 filter的加载顺序
servlet 、listener和 filter的加载顺序与它们在web.xml 文件中的先后顺序无关。并不是注册在web.xml 文件中前面的会被先加载,通过他们各自的作用。可以很显然的得到一个结论:listener 最先加载,其次是filter ,最后是 servlet。但是,如果还配置了context-param,它用于向 ServletContext 提供键值对,即应用程序上下文信息。由于listener, filter 等在初始化时会用到这些上下文中的信息,所以 context-param配置的东西会被最先加载。
例如,spring的配置文件的加载,需要在web.xml文件中注册:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring/spring.xml
</param-value>
</context-param>