Filter过滤器详解

1.Filter(过滤器)是Servlet 2.3中新增的技术,其基本功能就是对Servlert容器调用Servlet的过程进行拦截,从而在Servlet进行响应处理的前后实现一些特殊功能。

2.Filter的基本工作原理:Filter程序是一个实现了特殊接口(javax.servlet.Filter)的Java类,与Servlet程序相似,它也是由Servlet容器进行调用和执行的。Filter程序需要在web.xml文件中进行注册和设置它所能拦截的资源,由于不管什么类型的资源,最终都是以Servlet程序的形式来运行的。例如:JSP页面要被翻译成Servlet(通过tomcat下的conf目录下的web.xml中配置的名为JSP的servlet进行翻译)、静态图片文件和HTML文件是由默认servlet负责处理的(tomcat下的conf目录下的web.xml中配置的名为default的servlet进行翻译)。

   当在web.xml文件中注册了一个Filter来对某个Servlet程序进行拦截处理时,这个Filter就成了Servlet容器与该Servlet程序的通信线路上的一道关卡,它可以对Servlet容器发送给Servlet程序的请求和Servlet程序回送给Servlet容器的响应进行拦截,可以决定是否将请求继续传递给Servlet程序,以及对请求和响应消息是否进行修改。当Servlet容器开始调用某个Servlet程序时,如果发现已经注册了一个Filter程序来对该Servlet进行拦截,那么,Servlet容器将不再直接调用Servlet的service方法,而是调用Filter的doFilter方法,再由doFilter方法决定是否去激活Servlet的service方法。Filter的基本工作原理如图所示:


3.在一个Web应用程序中可以注册多个Filter程序,每个Filter程序都可以对一个或一组Servlet程序进行拦截。如果有多个Filter程序都可以对某个Servlet程序的访问过程进行拦截,当针对该Servlet的访问请求到达时,Web容器将把这多个Filte程序组合成一个Filter链(也叫过滤器链接)。Filter链中的各个Filter的拦截顺序与它们在应用程序的web.xml文件中的映射顺序一致,上一个Filter的doFilter方法中调用FilterChain.doFilter方法将激活下一个Filter的doFilter方法,最后一个Filter的doFilter方法中调用的FilterChain.doFilter方法将激活目标Servlet的service方法。如图所示:如图可知,只要Filter链中有任意一个Filter没有调用FilterChain.doFilter方法,那么,目标Servlet的service方法都将不会执行。


4.一个Filter程序就是一个Java类,它必须实现javax.servlet.Filter接口,javax,servlet.Filter接口中定义了三个方法:init、doFilter、destory。

(1)init:和Servlet一样,这个方法是用来进行初始化操作的,init方法在Filter的生命周期中仅执行一次。

public void init(FilterConfig filterConfig) throws ServletException {}

从filterConfig中能获取到web.xml中对当前Filter的配置文件信息

例如:

web.xml

<filter>
  	<filter-name>firstFilter</filter-name>
  	<filter-class>com.xxc.filterTestt.FilterTest</filter-class>
  	<init-param>
  		<param-name>password</param-name>
  		<param-value>123</param-value>
  	</init-param>
</filter>
init方法

public void init(FilterConfig filterConfig) throws ServletException {
	//Servlet规范将代表ServletContext对象和Filter的配置参数信息都封装到一个FilterConfig对象中
	ServletContext context = filterConfig.getServletContext();//获取ServletContext
	String passwod = filterConfig.getInitParameter("password");//获取web.xml中对当前Filter对象的配置的配置信息,如果参数名不存在则返回null
}

FilterConfig中的方法有:

1.getFilterName方法:

用于返回在web.xml文件中为Filter所设置的友好名称,也就是返回<filter-name>元素的设置值。

2.getServletContext方法:

用于返回FilterConfig对象中所包装的ServletContext对象的引用。

3.getInitParameter方法:

用于返回在web.xml文件中为Filter所设置的某个名称的初始化参数值,如果指定名称的初始化参数不存在,则返回值为null。

4.getInitParameterNames方法

用于返回一个Enumeration集合对象,该集合对象中包含在web.xml文件中为当前Filter设置的所有初始化参数的名称。



(2)doFilter:当一个Filter对象能够拦截的访问请求到达后,Servlet容器将调用该Filter对象的doFilter方法。只有Filter对象的init方法执行成功后,Filter对象才会被加入到Filter链中,该Filter对象的doFilter方法才会被调用。

public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {}
(3)destroy:该方法在Web容器卸载Filter对象之前被调用,显然,该方法在Filter对象的生命期中也仅执行一次。在此方法中释放被该Filter对象打开的资源,例如,关闭数据库连接和IO流。
public void destroy() {}
5.FilterChain接口:FilterChain接口的doFilter方法用于通知Web容器把请求交给Filter链中的下一个Filter去处理,如果当前调用此方法的Filter对象是Filter链中的最后一个Filter,那么将把请求交给目标Servlet程序去处理。可见,Filter链的起点是第一个Filter程序,Filter链的末端实际上就是目标Servlet程序。

6.像如下这么配置Filter,那么当访问index.jsp的时候就会被同一个拦截器拦截3次。拦截顺序按照在web.xml配置的先后顺序进行拦截。

<filter-mapping>
  	<filter-name>firstFilter</filter-name>
  	<url-pattern>*.jsp</url-pattern>
  	<servlet-name>jsp</servlet-name>
</filter-mapping>
<filter-mapping>
  	<filter-name>firstFilter</filter-name>
  	<servlet-name>jsp</servlet-name>
</filter-mapping>


7.<filter-mapping>标签里有一个<dispatcher>子元素,这个子元素有4个值:REQUEST、INCLUDE、FORWARD和ERROR,如果写了这个子元素,他们代表的是当前Filter的<url-pattern>里的页面所调用的方式必须满足子元素里写的跳转的方式才能触发Filter。默认是REQUEST跳转。
例如:这时在浏览器中访问abc.jsp(不存在的),首先满足了404,跳转到index.jsp而且又是Error跳转的方式,这时被firstFilter拦截器拦下  最后浏览器显示     xxxxxxxxxxxxxxxxx  index.jsp页面!!! yyyyyyyyyyyyyyyyyyy

web.xml

<error-page>
  	<error-code>404</error-code>
  	<location>/index.jsp</location>
</error-page>
<filter-mapping>
  	<filter-name>firstFilter</filter-name>
  	<url-pattern>*.jsp</url-pattern>
	<dispatcher>FORWARD</dispatcher>
	<dispatcher>ERROR</dispatcher>
</filter-mapping>

filter

public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		response.setCharacterEncoding("UTF-8");
		response.getWriter().println("xxxxxxxxxxxxxxxxx");
		chain.doFilter(request, response);
		response.getWriter().println("yyyyyyyyyyyyyyyyyyy");
}

index.jsp

<body>
	index.jsp页面!!!
</body>


如果将web.xml内容改成如下内容:这样做的目的是可以看到当ERROR跳转到index.jsp页面后,index.jsp FORWARD到index1.jsp最后到底是  ERROR 和FORWARD都执行呢?还是只执行ERROR。

<error-page>
  	<error-code>404</error-code>
  	<location>/index.jsp</location>
</error-page>
  
<filter>
  	<filter-name>firstFilter</filter-name>
  	<filter-class>com.xxc.filterTestt.FilterTest</filter-class>
</filter>
<filter>
  	<filter-name>firstFilter1</filter-name>
  	<filter-class>com.xxc.filterTestt.FilterTest1</filter-class>
</filter>
  
<filter-mapping>
  	<filter-name>firstFilter</filter-name>
  	<url-pattern>*.jsp</url-pattern>
	<dispatcher>ERROR</dispatcher>
</filter-mapping>
<filter-mapping>
  	<filter-name>firstFilter1</filter-name>
  	<url-pattern>*.jsp</url-pattern>
	<dispatcher>FORWARD</dispatcher>
</filter-mapping>

filter

public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		response.setCharacterEncoding("UTF-8");
		response.getWriter().println("xxxxxxxxxxxxxxxxx");
		chain.doFilter(request, response);
		response.getWriter().println("yyyyyyyyyyyyyyyyyyy");
}

filter1

public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		response.getWriter().println("22222222222222222Filter----start");
		chain.doFilter(request, response);
		response.getWriter().println("22222222222222222Filter-----end");
}



如果将index.jsp页面内容改成如下:

<body>
	index.jsp页面!!!
	<jsp:forward page="/index1.jsp"></jsp:forward>
</body>


index1.jsp页面

<body>
    index1.jsp页面!!!!
</body>


这时在浏览器中访问abc.jsp(不存在的),首先满足了404,跳转到index.jsp,发现index.jsp又要forward到index1.jsp中,所以这时又跳转到index1.jsp中。由于最开始ERROR跳转(后面再进行了什么跳转不被响应的Filter拦截下来),所以最后的结果是

xxxxxxxxxxxxxxxxx index1.jsp页面!!!! yyyyyyyyyyyyyyyyyyy


8.如果页面中有这样的错误页面处理代码,结果是按Forward跳转的而不是ERROR。

<%@page errorPage="haha.html" %>

9.新闻页面系统,当点击新闻链接的时候需要检查用户是否登录,如果已经登录则显示链接页面。 如果未登录,则先要进行登录操作,然后页面跳转到刚才点击的超链接上,而不是index.jsp了。

在这个例子中有一点需要注意的是:request.getRequestURI()表示你要去哪张页面,request.getHeader("Referer");表示你从哪张页面跳转到当前页面的。千万别搞混!

例子:

a.jsp

<a href="b.jsp">b.jsp</a>

b.jsp

<%
		String uri = request.getRequestURI();
		String rRefe = request.getHeader("Referer");
		System.out.println(uri); //表示要访问哪个页面 /servletday7/b.jsp
		System.out.println(rRefe); //表示从哪个页面来http://localhost:9090/servletday7/a.jsp
%>


10.Filter解决get和post提交的中文乱码解决方法:

原理:需要在request里做处理,将参数的值new String(value.getBytes("ISO-8859-1"),"UTF-8")这样转换下

filter

package com.xxc.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 com.xxc.codeUtil.XxcChineseCode;

public class ChineseCodeFilter implements Filter{
	private String enCoding;
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		enCoding = filterConfig.getInitParameter("enCoding");
	}

	@Override
	public void doFilter(ServletRequest req,
			ServletResponse res, FilterChain filterChain)
			throws IOException, ServletException {
		req.setCharacterEncoding(enCoding);
		HttpServletRequest request = (HttpServletRequest)req;
		if("GET".equals(request.getMethod())){//判断表单是什么提交方式
			request = new XxcChineseCode(request,enCoding);//创建一个request的包装类
		}
		filterChain.doFilter(request, res);
	}

	@Override
	public void destroy() {
		
	}

}

web.xml

<filter>
		<filter-name>chinersCodeFilter</filter-name>
		<filter-class>com.xxc.filter.ChineseCodeFilter</filter-class>
		<init-param>
			<param-name>enCoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
</filter>

<filter-mapping>
		<filter-name>chinersCodeFilter</filter-name>
		<url-pattern>/servlet/*</url-pattern>
</filter-mapping>


HttpServletRequest的包装类,这个类需要继承HttpServletRequestWrapper,这样就拥有了HttpServletRequest的所有方法,我们需要覆写的是获取URL参数的值的方法即可。对GET提交有效,POST无效。因为获取参数的方法有多种,所以都要覆写。其实就是把每个参数转换下new String(value.getBytes("ISO-8859-1"),"UTF-8")

package com.xxc.codeUtil;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class XxcChineseCode extends HttpServletRequestWrapper{
	private String enCoding;
	
	public XxcChineseCode(HttpServletRequest request,String enCoding) {
		super(request);
		this.enCoding = enCoding;
	}

	public String getParameter(String name) {
		String value = super.getParameter(name);
		try {
			value = changeCode(value);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return value;
	}
	
	@Override
	public String[] getParameterValues(String name) {
		String[] values = super.getParameterValues(name);
		if(values==null){
			return null;
		}
		for(int i=0;i<values.length;i++){
			try {
				values[i] = changeCode(values[i]);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		return values;
	}
	
	@Override
	public Map<String, String[]> getParameterMap() {
		Map<String,String[]> map = super.getParameterMap();
		Map<String,String[]> result = new HashMap<String,String[]>();
		Set<Entry<String, String[]>> entrySet = map.entrySet();
		for(Entry<String, String[]> set : entrySet){
			String paramName = set.getKey();
			String[] paramValue = set.getValue();
			for(int i=0;i<paramValue.length;i++){
				try {
					paramValue[i] = changeCode(paramValue[i]);
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
			result.put(paramName, paramValue);
		}
		return result;
	}
	
	private String changeCode(String value) throws UnsupportedEncodingException {
		return new String(value.getBytes("ISO-8859-1"),enCoding);
	}
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值