Filter 学习
# 135.Filter 学习
Servlet,Filter 和 Listener 被称为 JavaWeb 的三大组件,今天我们就开始学习 Filter(重点掌握)和 Listener(了解)
# 什么是 Filter
Filter,就是过滤器的意思,例如:
生活中的过滤器:净水器, 空气净化器
JavaWeb 中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能,例如有些功能是得登录后才能使用,如果我们在每个 Servlet 里都加上判断是否登录的代码,就非常难以维护。
我们可以加个过滤器,有请求时先判断用户是否登录了,是则放行这个请求,这样就减少了重复代码
过滤器的作用:一般用于完成通用的操作。如:登录验证、统一字符编码处理(比如我们之前的 doPost 方法里要设置编码为 UTF8)、敏感字符过滤(比如游戏中不能骂人)...
# 快速入门
编写过滤器的步骤:
- 定义一个类,实现接口 Filter
- 复写 Filter 的方法
- 配置拦截路径: 例如注解
@WebFilter("/*")
,或者通过 web.xml
我们写个 Servlet 演示下:
package com.peterjxl.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*") // 拦截路径,这样访问所有资源之前都会执行该过滤器
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterDemo1被执行了。。。。");
// 放行请求,如果不放行则相当于不返回数据给浏览器
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
注意:如果不放行,访问任何资源都是不会有数据返回给浏览器的
也可以通过 web.xml 配置:
<filter>
<filter-name>demo1</filter-name>
<filter-class>com.peterjxl.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>demo1</filter-name>
<!-- 配置拦截路径 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
2
3
4
5
6
7
8
9
# WebFilter
源码
我们可以看看注解 WebFilter
的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebFilter {
String description() default "";
String displayName() default "";
WebInitParam[] initParams() default {};
String filterName() default "";
String smallIcon() default "";
String largeIcon() default "";
String[] servletNames() default {};
String[] value() default {};
String[] urlPatterns() default {};
DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
boolean asyncSupported() default false;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ElementType.TYPE 表明可以使用在类上,
RUNTIME 表明保留到运行阶段
Documented 表明生成文档
其他的属性我们可以忽略,主要是看 urlPatterns,和 WebServlet 的注解差不多
# 过滤器执行流程
- 执行过滤器
- 执行放行后的资源
- 回来执行过滤器放行代码下边的代码
举个生活的例子:你坐公交去会所,公交车是一个过滤器,交钱后给你放行;等你从会所回来,又坐公交车,那么还得交钱,然后放行。相当于原路返回。
package com.peterjxl.filter;
import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebFilter("/*")
public class FilterDemo2 implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
//对request对象请求消息增强,例如判断是否登陆
System.out.println("filterDemo2执行了....");
//放行
chain.doFilter(request, response);
//对response对象的响应消息增强,例如判断登陆成功,在响应消息中设置登录信息
System.out.println("filterDemo2回来了...");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
需要注意的是,过滤器中拿到的 request 对象和具体 Servlet 中拿到的 request 对象是同一个,因为我们 doFilter 里传的就是 request 和 response 对象
重启 Tomcat,访问 index.jsp,可以看到输出了
filterDemo2执行了....
filterDemo2回来了...
2
# 过滤器生命周期方法
Filter
接口有 3 个方法,分别代表 3 个生命周期:
init
: 在服务器启动后,会创建 Filter 对象,然后调用 init 方法。该方法只执行一次,常用于加载资源doFilter
: 每一次请求被拦截资源时,会执行。一般会执行多次destroy
: 如果服务器是正常关闭,则会执行 destroy 方法, Filter 对象被销毁。只执行一次,常用于释放资源
我们可以写个 Servlet,然后关闭和启动一次 Tomcat,观察方法是否被执行了
package com.peterjxl.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 演示生命周期
*/
@WebFilter("/*")
public class FilterDemo3 implements Filter {
public void init(FilterConfig config) throws ServletException {
System.out.println("FilterDemo3 Init....");
}
public void destroy() {
System.out.println("FilterDemo3 destroy....");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
chain.doFilter(request, response);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 通过路径和方式配置过滤器
我们访问资源的时候,除了浏览器访问之外,还可以通过转发的方式访问;而过滤器可以根据请求的方式来配置是否执行过滤器,例如转发的时候就不执行过滤器。
拦截路径配置方式:
- 具体资源路径: /index.jsp 只有访问 index.jsp 资源时,过滤器才会被执行
- 拦截目录: /user/* 访问/user 下的所有资源时,过滤器都会被执行
- 后缀名拦截: *.jsp 访问所有后缀名为 jsp 资源时,过滤器都会被执行
- 拦截所有资源:/* 访问所有资源时,过滤器都会被执行
拦截方式配置:资源被访问的方式。例如设置浏览器直接请求资源,才能被拦截器拦截,这样服务器内部转发的就不会被拦截,可以通过注解和 web.xml 配置。
通过注解配置拦截方式:设置 dispatcherTypes 属性, 可以设置多个,例如
@WebFilter(value="/index.jsp" dispatcherTypes = DispatcherType.Request)
有如下访问方式:
- REQUEST:默认值,也就是浏览器直接请求资源的方式
- FORWARD:转发访问资源的方式,如果过滤器配置了这个属性,那么只有转发的资源才会被过滤器执行
- INCLUDE:包含访问资源,了解即可
- ERROR:错误跳转资源,了解即可
- ASYNC:异步访问资源,SYNC 就是同步,加个 A 就是反义词,因此 ASYNC 就是异步,了解即可
可以配置多个拦截方式:
//浏览器直接请求index.jsp或者转发访问index.jsp,该过滤器会被执行
@WebFilter(value="/index.jsp",dispatcherTypes ={ DispatcherType.FORWARD, DispatcherType.REQUEST})
2
web.xml 配置:在 filter-mapping
下 设置 <dispatcher></dispatcher>
标签即可
# 过滤器链(配置多个过滤器)
执行顺序:如果有两个过滤器,分别是过滤器 1 和过滤器 2,假设先执行了过滤器 1,则过滤器的执行流程为:
- 过滤器 1
- 过滤器 2
- 资源执行
- 过滤器 2 剩下的代码
- 过滤器 1 剩下的代码
更多过滤器的情况同理,相当于原路返回
还是以坐公车去会所为例,相当于要转车,去到目的地获取想要的资源;然后回来的时候还是一样,原路返回,转车。
那么怎么判断哪个过滤器先执行呢?有如下规则:
- 注解配置:按照类名的字符串比较规则比较,值小的先执行。如: AFilter 和 BFilter,AFilter 就先执行了。
- web.xml 配置:
<filter-mapping>
谁定义在上边,谁先执行
我们可以创建两个 Servlet 来演示下:
package com.peterjxl.filter;
import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebFilter("/*")
public class FilterDemo6 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
System.out.println("filterDemo6执行了...");
chain.doFilter(request, response);
System.out.println("filterDemo6回来了...");
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.peterjxl.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class FilterDemo7 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
System.out.println("filterDemo7执行了...");
chain.doFilter(request, response);
System.out.println("filterDemo7回来了...");
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
重启 Tomcat,随便访问某个路径,控制台输出:
filterDemo6执行了...
filterDemo7执行了...
filterDemo7回来了...
filterDemo6回来了...
2
3
4
- 01
- 中国网络防火长城简史 转载10-12
- 03
- 公告:博客近期 RSS 相关问题10-02