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设置的所有初始化参数的名称。
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>
例如:这时在浏览器中访问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>
<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>
<body>
index1.jsp页面!!!!
</body>
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);
}
}