1、概念
Filter,过滤器。其主要用于过滤请求,是服务器端的三大组件之一
服务器端的三大组件:Servlet、Filter、Listener
三大组件的特点:
⑴ 都需要运行在服务器端
⑵ 都需要实现某个接口
⑶ 都需要在web.xml中注册
2、创建Filter的步骤
⑴ 创建一个类,让其实现Filter接口,并实现抽象方法
⑵ 在web.xml中进行注册
服务器会根据web.xml中配置的Filter的全类名,来创建实例,并过滤请求
示例:
【创建一个类,实现Filter接口】
public class MyFilter implements Filter {}
【在web.xml中进行注册】
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.test.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<!-- 映射请求的路径 -->
<url-pattern>/hellofilter.jsp</url-pattern>
</filter-mapping>
3、Filter的作用
⑴ 在请求到达目标资源之前,拦截该请求
⑵ 放行请求
⑶ 在响应到达浏览器之前,做一些其他的操作
4、Filter的声明周期
Filter从创建到销毁,体现在4个方法中:
⑴ 构造方法
① 服务器一启动,就会调用该方法,并创建Filter对象。说明Filter对象是在服务器启动时被创建的
② 该方法在Filter的整个生命周期中,只会被调用一次。说明Filter是单例的
⑵ init方法
public void init(FilterConfig filterConfig);
① 该方法在Filter的构造方法被调用后,就立即被调用。其主要用于对Filter对象做一些初始化的操作
② 该方法在Filter的整个生命周期中,只会被调用一次
参数FilterConfig对象的作用
和Servlet的init方法中的ServletConfig对象类似
⑴ 获取Filter在web.xml中注册的名字
public String getFilterName();
⑵ 获取ServletContext对象
public ServletContext getServletContext();
⑶ 获取初始化参数
public String getInitParameter(String name);
根据param-name,获取在web.xml中的filter标签中的init-param里的param-value的值
<filter>
<init-param>
<param-name>key</param-name>
<param-value>value</param-value>
</init-param>
</filter>
示例:
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 获取FilterName
String filterName = filterConfig.getFilterName();
System.out.println(filterName);
// 获取ServletContext对象
ServletContext servletContext = filterConfig.getServletContext();
System.out.println(servletContext);
// 获取初始化参数
String value = filterConfig.getInitParameter("key");
System.out.println(value);
}
⑶ doFilter方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain);
① 该方法,在每次访问Filter在web.xml中注册的,映射的请求路径【url-pattern】时,都会被调用。其用来拦截请求
② 该方法在Filter的整个生命周期中,会被调用多次
③
⒈ ServletRequest和ServletResponse对象,和Servlet的service方法中的ServletRequest和ServletResponse对象一样
⒉ FilterChain对象是用来放行请求的。需要调用其doFilter方法
public void doFilter(ServletRequest request, ServletResponse response);
将request和response对象传进去
⑷ destroy方法
public void destroy();
① 该方法在服务器关闭前被调用,用于销毁Filter对象
② 该方法在Filter的整个生命周期中,只会被调用一次
5、多个过滤器的执行顺序
可以为同一个资源设置多个过滤器,多个过滤器组成一个过滤器链。
多个过滤器的执行顺序,由在web.xml中的
<filter-mapping>
标签决定
Tips:
⑴ 声明在前的,先执行;声明在后的,后执行
⑵ 如果多个过滤器都放行了请求,则FilterChain对象的doFilter方法,上面的代码按照声明的先后顺序被执行;而下面的代码的执行顺序,则跟声明的顺序相反
注意:tomcat6.0中,filter-mapping标签必须在对应的filter标签的下面,否则会报错。而在tomcat7.0中,filter-mapping则可以在filter的上面
示例:
【OrderFilter1】
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("第1被执行");
chain.doFilter(request, response);
System.out.println("第5被执行");
}
【OrderFilter2】
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("第2被执行");
chain.doFilter(request, response);
System.out.println("第4被执行");
}
【index.jsp】
<body>
<% System.out.println("第3被执行"); %>
</body>
【web.xml配置文件】
<filter>
<filter-name>OrderFilter1</filter-name>
<filter-class>com.test.filter.OrderFilter1</filter-class>
</filter>
<filter-mapping>
<!-- 先注册的 -->
<filter-name>OrderFilter1</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
<filter>
<filter-name>OrderFilter2</filter-name>
<filter-class>com.test.filter.OrderFilter2</filter-class>
</filter>
<filter-mapping>
<!-- 后注册的 -->
<filter-name>OrderFilter2</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
6、url-pattern的配置规则
精确匹配
配置一个完整的路径
<url-pattern>/index.jsp</url-pattern>
只有访问index.jsp时,过滤器才会拦截请求
Tips:/ 代表当前项目的根路径
注意:一定要在路径前加上/ ,否则tomcat无法正常启动
模糊匹配
前缀匹配
拦截以指定路径开头的资源
<url-pattern>/pages/*</url-pattern>
只有访问pages路径下的资源时,过滤器才会拦截请求
后缀匹配
拦截以指定字符结尾的资源
<url-pattern>*.jsp</url-pattern>
当访问的是jsp页面时,过滤器才会拦截请求
注意事项
不能同时添加前缀匹配和后缀匹配,例如
<url-pattern>/pages/*.jsp</url-pattern>
这样配置是无效的,会导致tomcat无法正常启动,会报错:IllegalArgumentException: Invalid /pages/*.jsp in filter mapping
拦截Servlet
在filter-mapping中加url-pattern标签
示例:
<filter-mapping>
<url-pattern>/loginServlet</url-pattern>
</filter-mapping>
注意:该url-pattern必须和要拦截的Servlet在web.xml中注册的url-pattern一致
在filter-mapping中加servlet-name标签
示例:
<filter-mapping>
<servlet-name>LoginServlet</servlet-name>
</filter-mapping>
注意:该servlet-name必须和要拦截的Servlet在web.xml中注册的servlet-name一致
7、dispatcher的配置
概念
Filter默认只会拦截直接请求,即直接访问某页面的请求。但是像请求的转发就不能拦截,要想拦截这样的请求,就必须在filter-mapping的子标签dispatcher中配置
<filter-mapping>
<dispatcher></dispatcher>
</filter-mapping>
REQUEST
默认值。只能拦截直接请求【直接访问页面的请求】
注意:如果配置了其他想要拦截的请求,同时还想拦截直接访问的请求,则需要同时配置该请求
示例:【拦截对page.jsp页面的直接请求】
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>com.test.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/page.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
FORWARD
拦截转发的请求
包括通过getRequestDispatcher().forward(); 方法或动作标签转发的请求
示例:【拦截对page.jsp的转发请求和直接请求】
【index.jsp】
<jsp:forward page="/page.jsp"></jsp:forward>
【web.xml】
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>com.test.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/page.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
INCLUDE
拦截动态包含
即通过动作标签,动态包含的页面,会被拦截。因为它调用了org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, “/动态包含的页面”, out, false); 方法
但是通过<%@ include %>指令标签,静态包含的页面,无法被拦截。因为它是直接将页面的内容复制过来的
示例:【拦截对page.jsp的动态包含】
【index.jsp】
<jsp:include page="/page.jsp"></jsp:include>
【web.xml】
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>com.test.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/page.jsp</url-pattern>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
ERROR
拦截在web.xml中配置的错误页面
注意:url-pattern标签的路径和error-page标签的子标签location的路径要一致
在web.xml中配置错误页面
<error-page>
<error-code></error-code>
<location></location>
</error-page>
error-code:错误状态码
location:错误页面的地址
注意:该地址需要以/ 开头【由服务器解析】
使用示例
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>com.test.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<!-- 要拦截的错误页面的地址 -->
<url-pattern>/404.jsp</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<error-page>
<error-code>404</error-code>
<!-- 错误页面的地址 -->
<location>/404.jsp</location>
</error-page>
8、HttpFilter
系统并没有提供doFilter方法中的request和response的对象是HttpServletRequest和HttpServletResponse,所以我们可以自己写一个,以便子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 javax.servlet.http.HttpServletResponse;
public abstract class HttpFilter implements Filter {
// FilterConfig属性
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
init();
}
// 供子类重写的init方法
public void init() {
}
// 提供公共的获取FilterConfig的get方法
public FilterConfig getFilterConfig() {
return filterConfig;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 向下转型
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 调用重载的抽象方法
doFilter(req, resp, chain);
}
// 供子类重写的抽象方法
public abstract void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException;
@Override
public void destroy() {
}
}