1、什么是过滤器
过滤器是一个程序,它先于与之相关的Servlet页面运行在服务器上,它是Servlet技术中最激动人心的技术,但它并不是一个标准的Servlet,它不能处理用户请求,也不能对客户端生成响应。它主要用于对HttpServletRequest进行预处理,也可以对HttpServletResponse进行后处理。
过滤器可以附加到一个或多个Servlet、JSP页面或者是HTML静态页面上,然后检查进入这些资源的请求信息。它对这些请求进行拦截,从而实现一些特殊的功能,比如可以实现权限访问控制,过滤敏感词汇,设置统一字符集、压缩响应信息等功能。
1、过滤器位于客户端和web应用程序之间,用于检查和修改两者之间流过的请求和响应;
2、在请求到达Servlet或者JSP之前,过滤器截获请求;
3、在响应送给客户端之前,过滤器截获响应;
4、多个过滤器可以形成一个过滤器链,过滤器链中不同过滤器的先后顺序由部署文件web.xml中
过滤器映射<filter-mapping>的顺序决定。
注意第三点:“在响应送给客户端之前,过滤器截获响应”!
2、过滤器的原理
在Servlet作为过滤器使用时,它可以对客户的请求进行处理。处理完成之后,它会交给下一个过滤器处理,这样,客户的请求在过滤器链中逐个处理,直到请求发送到目标为止。例如,我们访问用户管理系统的某个页面,服务器在进行处理时需要做两项工作:①判断客户端的会话是否有效然后检查权限;②对提交的数据进行统一编码。这两项工作可以在由两个过滤器组成的过滤器链中进行处理。当过滤器处理成功后,再把提交的数据发送到最终目标。
可以看一下过滤器链在整个Web应用中的位置:
3、过滤器的使用
(1) 开发步骤
开发Servlet过滤器的步骤为:
1.编写实现Filter接口的Servlet类。
2.在web.xml中配置Filter
开发一个过滤器需要实现Filter接口,Filter接口定义了以下方法:
1、destory()由web容器调用,销毁此Filter
2、init(FilterConfig filterConfig)由Web容器调用,初始化此Filter
3、doFilter(ServletRequest request, ServletResponse response, FilterChain chain)具体处理过滤的代码
一个过滤器的代码类似如下,其中最重要的是在doFilter()方法中的实现自己对request和response的过滤:
public class MyFilter implements Filter {
@Override
public void destroy() {
//销毁代码
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain chain) throws IOException, ServletException {
//具体的过滤细节
}
@Override
public void init(FilterConfig arg0) throws ServletException {
//初始化代码
}
}
(2) Filter链
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。
FilterTest1.java的代码:
public class FilterTest1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 做全局性的设置(对request和response进行一些预处理)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("FilterTest1执行之前!!!");
// 拦截下目标资源,然后放行(目标资源也会执行)
chain.doFilter(request, response); // 放行
System.out.println("FilterTest1执行之后!!!");
}
@Override
public void destroy() {
}
}
FilterTest2.java的代码:
public class FilterTest2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("FilterTest2执行之前!!!");
// 拦截下目标资源,然后放行(目标资源也会执行)
chain.doFilter(request, response); // 放行
System.out.println("FilterTest2执行之后!!!");
}
@Override
public void destroy() {
}
}
在web.xml中配置过滤器,注意:Filter在web.xml文件中是有注册顺序。
我们可能这样配置,说实话我们经常这么做不是吗?
<filter>
<filter-name>FilterTest1</filter-name>
<filter-class>cn.itcast.web.filter.FilterTest1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterTest1</filter-name>
<!--“/*”表示拦截所有的请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>FilterTest2</filter-name>
<filter-class>cn.itcast.web.filter.FilterTest2</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterTest2</filter-name>
<!--“/*”表示拦截所有的请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
从以上配置中,关于Filter在web.xml文件中的注册顺序我们是一目了然的,即FilterTest1过滤器在前,FilterTest2过滤器在后。但我们有时候又会这样配置:
<filter>
<filter-name>FilterTest1</filter-name>
<filter-class>cn.itcast.web.filter.FilterTest1</filter-class>
</filter>
<filter>
<filter-name>FilterTest2</filter-name>
<filter-class>cn.itcast.web.filter.FilterTest2</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterTest1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FilterTest2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
那这样是不是说只要哪个标签在前,哪个过滤器就在前呢?可惜并不是这样的,在web.xml中配置的过滤器的先后顺序是依的顺序而定。我们只要了解这点就好,没人会闲的蛋疼的考你这方面的知识。
网站首页index.jsp的代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
System.out.println("index!!!");
%>
</body>
</html>
最后在浏览器中输入访问服务器的地址http://localhost:8080/day19/index.jsp,将在Eclipse的控制台打印:
注意:
从这个例子我们可以观察到,过滤链在服务器处理完请求后,会拦截应答进行再一次的处理(如果过滤器有设置的话)!!并且过滤顺序跟一开始是相反的。
4、过滤器的部署
Filter的部署分为两个步骤:
1、注册Filter
2、映射Filter
过滤器的部署细节与Servlet很相似,Servlet是要配置<servlet>和<servlet-mapping>,
而过滤器是要配置<filter>和<filter-mapping>。
(1) 注册Filter
开发好Filter之后,需要在web.xml文件中进行注册,这样才能够被web服务器调用。
1、简单配置如下所示(假设我的Filter放在com.gavin.filter包下):
<filter>
<filter-name>FilterName</filter-name>
<filter-class>com.gavin.filter.FilterName</filter-class>
</filter>
2、在web.xml文件中注册Filter详细范例:
<filter>
<filter-name>testFitler</filter-name>
<filter-class>org.test.TestFiter</filter-class>
<init-param>
<param-name>word_file</param-name>
<param-value>/WEB-INF/word.txt</param-value>
</init-param>
</filter>
1、<filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
2、<filter-class>元素用于指定过滤器的完整的限定类名。
3、<init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。在过滤器中,
可以使用FilterConfig接口对象来访问初始化参数。如果过滤器不需要指定初始化参数,那么<init-param>元素可以不配置。
(2) 映射Filter
在web.xml文件中注册了Filter之后,还要在web.xml文件中映射Filter。
<!--映射过滤器-->
<filter-mapping>
<filter-name>testFitler</filter-name>
<!--“/*”表示拦截所有的请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>元素用于设置一个Filter所负责拦截的资源。
一个Filter拦截的资源可通过两种方式来指定:Servlet名称和资源访问的请求路径。
上面的示例是资源访问的请求路径的形式,下面咱们使用Servlet名称来指定Filter拦截的资源,例如:
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<servlet-name>ServletDemo1</servlet-name>
</filter-mapping>
元素的解释:
1、<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字。
2、<url-pattern>设置filter所拦截的请求路径(过滤器关联的URL样式)。
3、<servlet-name>指定过滤器所拦截的Servlet名称。
4、<dispatcher>指定过滤器所拦截的资源被Servlet容器调用的方式,可以是REQUEST、INCLUDE、FORWARD和ERROR之一,
默认REQUEST。用户可以设置多个<dispatcher>子元素用来指定Filter对资源的多种调用方式进行拦截。如下:
<filter-mapping>
<filter-name>testFilter</filter-name>
<url-pattern>/index.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<dispatcher>子元素可以设置的值及其意义:
1、request:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问或ERROR情况时,那么该过滤器就不会被调用。
2、include:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
3、forward:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
4、ERROR: 如若在A.jsp页面page指令中指定了error属性=examError.jsp,那么A.jsp中若出现了异常,会跳转到examError.jsp中处理。而在跳转到examError.jsp时,若过滤器配置了ERROR的dispather那么则会拦截,否则不会拦截。
5、过滤器的生命周期
(1) Filter的创建
和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web应用程序启动时,web服务器将创建Filter的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作。
注意:
1、filter对象只会创建一次,init方法也只会执行一次。
2、开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
(2) Filter的销毁
destroy方法在Web容器卸载Filter对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。
面试题:Filter对象的生命周期。
1、Filter对象何时被创建?
服务器一启动的时候,就会针对这个web应用将所有的Filter对象(拦截器)创建出来,并且以后访问的时候,都是使用同一个拦截器进行拦截。也即一个拦截器会被所有的请求所共享,每一次请求来了之后,都会导致doFilter()方法被调用一次,Filter对象只有一个,而doFilter()方法会被多次调用。
2、Filter对象在内存里面有几个?
答:一个。服务器并不会针对请求创建新的Filter对象(拦截器)。
Filter对象何时被摧毁?
移除掉web服务器里面这个web应用(或停掉服务器),就会摧毁这个web应用对应的拦截器。
咱们从一个具体示例中来理解:
public class FilterTest1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter被创建了!!!");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 做全局性的设置(对request和response进行一些预处理)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("FilterTest1执行之前!!!");
// 拦截下目标资源,然后放行(目标资源也会执行)
chain.doFilter(request, response); // 放行
System.out.println("FilterTest1执行之后!!!");
}
@Override
public void destroy() {
System.out.println("Filter被销毁了!!!");
}
}
当启动服务器的时候,Eclipse的控制台会打印Filter被创建了!!!如下:
当停掉服务器时,Eclipse的控制台会打印Filter被销毁了!!!如下:
(3) FilterConfig接口
用户在配置filter时,可以使用init-param为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:
String getFilterName():得到filter的名称。
String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null。
Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
public ServletContext getServletContext():返回Servlet上下文对象的引用。
范例:利用FilterConfig得到filter配置信息
public class FilterTest1 implements Filter {
/*
* Filter对象在被创建时,服务器会调用init()方法,并传递进来这样一个对象:FilterConfig(代表Filter的配置信息)
* FilterConfig对象封装了配置的初始化参数。
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter被创建了!!!");
String value = filterConfig.getInitParameter("xxx"); // 获取指定初始化参数
System.out.println(value);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 做全局性的设置(对request和response进行一些预处理)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("FilterTest1执行之前!!!");
// 拦截下目标资源,然后放行(目标资源也会执行)
chain.doFilter(request, response); // 放行
System.out.println("FilterTest1执行之后!!!");
}
@Override
public void destroy() {
System.out.println("Filter被销毁了!!!");
}
}
在web. xml中配置过滤器:
<filter>
<filter-name>FilterTest1</filter-name>
<filter-class>cn.itcast.web.filter.FilterTest1</filter-class>
<init-param>
<param-name>xxx</param-name>
<param-value>yyyy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterTest1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这样在启动服务器的时候,Eclipse的控制台会打印:
如果我们现在想在FilterTest1类的doFilter方法中使用FilterConfig对象封装的配置初始化参数,那又该怎么做呢?我们可以像下面这样做哟:
public class FilterTest1 implements Filter {
private FilterConfig config;
/*
* Filter对象在被创建时,服务器会调用init()方法,并传递进来这样一个对象:FilterConfig(代表Filter的配置信息)
* FilterConfig对象封装了配置的初始化参数。
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter被创建了!!!");
// String value = filterConfig.getInitParameter("xxx"); // 获取指定初始化参数
// System.out.println(value);
this.config = filterConfig;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String value = this.config.getInitParameter("xxx");
System.out.println(value);
// 做全局性的设置(对request和response进行一些预处理)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("FilterTest1执行之前!!!");
// 拦截下目标资源,然后放行(目标资源也会执行)
chain.doFilter(request, response); // 放行
System.out.println("FilterTest1执行之后!!!");
}
@Override
public void destroy() {
System.out.println("Filter被销毁了!!!");
}
}
6、过滤器的应用
过滤器可以附加到一个或多个Servlet、JSP页面或者是HTML静态页面上,然后检查进入这些资源的请求信息。它对这些请求进行拦截,从而实现一些特殊的功能,比如可以实现权限访问控制,过滤敏感词汇,设置统一字符集、压缩响应信息等功能。
(1) 统一全站字符编码
通过配置参数charset指明使用何种字符编码,以处理Html Form请求参数的中文问题。
// 解决全站中文乱码
public class CharacterEncodingFilter implements Filter {
private FilterConfig config;
private String defaultCharset = "UTF-8"; //默认值
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
// 获取要设置的字符集
String charset = this.config.getInitParameter("charset");
if (charset == null) {
charset = defaultCharset;
}
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request.setCharacterEncoding(charset);
response.setCharacterEncoding(charset);
response.setContentType("text/html;charset=" + charset);
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
}
@Override
public void destroy() {
}
}
在web.xml文件中配置以上过滤器:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class> cn.itcast.web.filter.example.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2) 防止非法登录
使用Session防止用户非法登录到某个页面,采用的是在登录页面将用户信息放入Session,在需要防止非法登录的页面取出登录的信息,如果没有登录用户的信息,则为非法登录,然后将其强制跳转到登录页面。
那么就出现了一个问题,如果我们的Web应用有很多个需要防止非法登录的页面怎么办?一个个都这样处理肯定非常麻烦。这时候,过滤器的应用就让这个问题变得非常简单。
首先,我们编写CheckUserFilter,它的doFilter()代码如下:
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)arg0;
HttpServletResponse response = (HttpServletResponse)arg1;
User login_user = (User)request.getSession().getAttribute("login-user");
if(login_user == null){
// 说明用户没有登录,让他跳转到错误页面
request.setAttribute("error", "请登录!");
request.getRequestDispatcher("/LoginServlet").forward(request,response);
}else{
// 让其通过
chain.doFilter(request, response);
}
}
可以看到,在doFilter中,我们去判断Session中是否有登录用户的信息,如果有,让其通过;如果没有,则强制跳转到登录页面。然后我们对这个Filter进行部署,假设我们的主页面MainFrame,添加用户的页面AddUser和更新用户的页面UpdateUser都需要防止非法登录,则可以让其映射到所有的这些Servlet上,部署如下:
<filter>
<filter-name>CheckUserFilter</filter-name>
<filter-class>com.gavin.filter.CheckUserFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CheckUserFilter</filter-name>
<servlet-name>MainFrame</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>CheckUserFilter</filter-name>
<servlet-name>AddUser</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>CheckUserFilter</filter-name>
<servlet-name>UpdateUser</servlet-name>
</filter-mapping>
实验可得,在没有登录之前去访问MainFrame、AddUser或者UpdateUser,都会提示错误信息,然后跳转到登录页面。
(3) 禁止浏览器缓存所有动态页面
有3个HTTP响应头字段都可以禁止浏览器缓存当前页面,它们在Servlet中的示例代码如下:
response.setDateHeader(“Expires”,-1);
response.setHeader(“Cache-Control”,”no-cache”);
response.setHeader(“Pragma”,”no-cache”);
并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是同时使用上面的三个响应头。
Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面。
Cache-Control响应头有两个常用值:
(1) no-cache:指浏览器不要缓存当前页面。
(2) max-age:xxx:指浏览器缓存页面xxx秒。
在Eclipse中新建一个day19_test的web项目,在该项目下创建网站首页index.jsp,代码为:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
System.out.println("index!!!");
%>
</body>
</html>
我们以IE 9浏览器作为测试工具,先清空IE浏览器的缓存,然后打开IE浏览器,输入访问服务器的地址http://localhost:8080/day19_test/index.jsp,这时查看IE浏览器的缓存目录,发现index.jsp这个web动态资源缓存在里面。
这时我们要禁止IE浏览器缓存所有动态页面,所以要编写一个禁止浏览器缓存所有动态页面的过滤器。
public class NoCacheFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
web.xml文件中的配置如下:
<filter>
<filter-name>NoCacheFilter</filter-name>
<filter-class>cn.itcast.web.filter.example.NoCacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<!-- 拦截所有jsp的访问 -->
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
这时再次清空IE浏览器的缓存,然后打开IE浏览器,输入访问服务器的地址http://localhost:8080/day19_test/index.jsp,会发现IE浏览器的默认缓存目录下没有缓存index.jsp页面。
(4) 控制浏览器缓存页面静态资源
有些动态页面中引用了一些图片或css文件以修饰页面效果,这些图片和css文件经常是不变化的,所以为减轻服务器的压力,可以使用filter控制浏览器缓存这些文件,以提升服务器的性能。
现在将day19_test该Web应用下的网站首页index.jsp修改为:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
Hello World!!!<br/>
<img src="${pageContext.request.contextPath }/images/Krystal.jpg">
</body>
</html>
这时我们就要创建一个控制浏览器缓存页面中的静态资源的过滤器。
// 控制浏览器缓存的过滤器
public class CacheFilter implements Filter {
private FilterConfig config;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 1. 获取到用户想访问的资源
String uri = request.getRequestURI();
// 2. 获取该资源的缓存时间
int expires = 0;
if (uri.endsWith(".jpg")) {
expires = Integer.parseInt(this.config.getInitParameter("jpg"));
} else if (uri.endsWith(".css")) {
expires = Integer.parseInt(this.config.getInitParameter("css"));
} else {
expires = Integer.parseInt(this.config.getInitParameter("js"));
}
// response.setDateHeader("expires", expires*60*1000); // 这个缓存时间值是1970-01-01 08:00:00到现在时刻的毫秒值,应该在当前时间值上加上一分钟,这是才缓存一分钟
response.setDateHeader("expires", System.currentTimeMillis() + expires*60*1000);
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
在编写上述过滤器时一定要注意一个细节:Expires相应头的值为GMT时间值,即从1970-01-01 08:00:00到现在时刻的毫秒值。应该在当前时间值上加上一分钟,这是才缓存一分钟。
web.xml文件中的配置如下:
<filter>
<filter-name>CacheFilter</filter-name>
<filter-class>cn.itcast.web.filter.example.CacheFilter</filter-class>
<!-- 配置要缓存的web资源以及缓存时间,以分钟为单位 -->
<init-param>
<param-name>css</param-name>
<param-value>10</param-value>
</init-param>
<init-param>
<param-name>jpg</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>js</param-name>
<param-value>20</param-value>
</init-param>
</filter>
<!-- 配置要缓存的web资源的后缀-->
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.jpg</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
注意:一个filter可以对应多个filter-mapping。
现在加上前面还有一个禁止浏览器缓存所有动态页面的过滤器,那么就有2个过滤器了,形成了一个过滤器链。打开IE浏览器,输入访问服务器的地址http://localhost:8080/day19_test/index.jsp,那么在IE浏览器默认的缓存目录里面将不会缓存index.jsp这个页面,但会缓存Krystal.jpg这个静态图片,而且缓存一分钟。如果在这一分钟之内,通过在IE浏览器的地址栏上敲回车键的行为去访问服务器,那么只会向服务器发送一次请求,即请求index.jsp这个动态Web资源,至于index.jsp页面中的Krystal.jpg静态图片,会从IE浏览器默认的缓存目录里面获取;如果通过点击刷新按钮的行为去访问服务器,那么将会向服务器发送二次请求,第一次请求index.jsp这个动态Web资源,第二次请求index.jsp页面中的Krystal.jpg静态图片。因为刷新有两个作用:
1、不管你有没有缓存,都要向服务器发请求。
2、刷新就是把上次的事情再干一次。
但我们平时访问网站,不是通过刷新去访问的,都是点击超链接。