FilterListener组件
文章目录
- Java Web三大组件
- 一、Filter概述
- 二、Filter开始
- 1_过滤器API介绍
- 2_过滤器开发步骤
- 3_代码实现
- 4_过滤器执行流程小结
- 三、使用细节
- 1_生命周期
- 2_拦截路径
- 3_过滤器链
- 四、Listener
- 1_Listener概述
- 2_监听器举例
- 3_Listener开始
- 4_案例:模拟spring框架
Java Web三大组件
- 组件: 是一个系统的组成部件
- javaweb组件 : javaweb项目的组成部件
- servlet
- filter
- listener
组件 | 作用 | 实现接口 |
---|---|---|
Servlet | Server Applet小应用程序,在JavaWeb中主要做为控制器来使用,可以处理用户的请求并且做出响应 | javax.servlet.Servlet |
Filter | 过滤器,对用户发送的请求或响应进行集中处理,实现请求的拦截 | javax.servlet.Filter |
Listener | 监听器,在Web执行过程中,监听一些事件,当相应事件发生时, 进行处理 | javax.servlet.XxxListener 每个事件有一个接口 |
一、Filter概述
生活中的过滤器
净水器、空气净化器、地铁安检
web中的过滤器
当用户访问服务器资源时,过滤器将请求拦截下来,完成一些通用的操作
F i l t e r Filter Filter 的作用
- 拦截客户端对web资源的请求 (重要!)
- 拦截web资源对客户端的响应
应用场景
如:登录验证、统一编码处理、敏感字符过滤
二、Filter开始
1_过滤器API介绍
F i l t e r Filter Filter 表示过滤器接口,我们想使用该接口必须自定义类实现接口并实现该接口中的所有抽象方法。
javax.Servlet.Filter
接口下面的三个方法:
方法 | 说明 |
---|---|
void init(FilterConfig filterConfig) | 过滤器对象创建的时候调用的方法 |
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | 执行过滤的方法,每次访问被拦截的资源都会执行该方法 |
void destory() | 过滤器销毁的时候调用的方法 |
注意:doFilter
第三个参数:FilterChain 表示过滤器链接口。
放行:使用 FilterChain 对象调用 FilterChain 中的方法:chain.doFilter(request,response);
即可以让浏览器访问服务器资源。
不放行,那么不写上述代码,即不让浏览器访问服务器资源。
2_过滤器开发步骤
1.自定义过滤器类实现过滤器接口 F i l t e r Filter Filter
2.在自定义类中实现过滤器接口 F i l t e r Filter Filter中的所有抽象方法
3.在doFilter
方法体中书写拦截资源的代码
F i l t e r Filter Filter 过滤器接口的关键方法说明:
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
参数:
-
request:表示请求对象,不满足 h t t p http http协议
-
response:表示响应对象,不满足 h t t p http http协议
-
chain:属于 FilterChain 的接口,表示过滤器链。
FilterChain 接口中具有一个放行方法:
void doFilter(ServletRequest request, ServletResponse response)
-
如果放行,希望浏览器可以访问拦截的资源则执行该方法
filterChain.doFilter(servletRequest,servletResponse);
-
如果不放行,不希望浏览器访问拦截的资源则不执行该方法
-
4.配置过滤器
5.访问被拦截的资源
3_代码实现
过滤器类:
import javax.servlet.*;
import java.io.IOException;
//1.自定义过滤器类实现过滤器接口Filter
public class MyFilter implements Filter {
//2.在自定义类中实现过滤器接口Filter中的所有抽象方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 不要调用父类的初始化方法否则报错
// Filter.super.init(filterConfig);
}
//3.在doFilter方法体中书写拦截资源的代码
//每次访问被过滤的资源都要执行该方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行过滤器了...");
//不放行
/*
Filter过滤器接口的:
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
参数:
request:表示请求对象,不满足http协议
response:表示响应对象,不满足http协议
chain:属于FilterChain的接口,表示过滤器链。
FilterChain接口中具有一个放行方法:
void doFilter(ServletRequest request, ServletResponse response)
如果放行,希望浏览器可以访问拦截的资源则执行该方法
如果不放行,不希望浏览器访问拦截的资源则不执行该方法
*/
//放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
web.xml
:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>Archetype Created Web Application</display-name>
<!--配置过滤器-->
<!--表示关联的过滤器类-->
<filter>
<filter-name>myFilter</filter-name>
<!--过滤器类的全路径,底层获取这里使用反射技术调用无参构造方法创建过滤器类的对象-->
<filter-class>com.example.sh.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<!--配置被拦截的资源-->
<url-pattern>/filter01.html</url-pattern>
</filter-mapping>
</web-app>
被拦截的页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>来拦截我啊</h1>
</body>
</html>
小结:
1.配置过滤器有两种方式:
-
xml
版本:<!--配置过滤器--> <!--表示关联的过滤器类--> <filter> <filter-name>myFilter</filter-name> <!--过滤器类的全路径,底层获取这里使用反射技术调用无参构造方法创建过滤器类的对象--> <filter-class>com.example.sh.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <!--配置被拦截的资源--> <url-pattern>/filter01.html</url-pattern> </filter-mapping>
-
注解版本配置
使用注解必须去掉
xml
版本。
4_过滤器执行流程小结
- 用户发送请求,请求Web资源(包括
html, jsp, servlet
等)。 - 如果Web资源的地址,是
f
i
l
t
e
r
filter
filter 要拦截的地址,请求将先经过
f
i
l
t
e
r
filter
filter,并执行
doFilter()
。 doFilter()
方法中如果调用filterChain.doFilter()
,则允许请求访问下一个Web资源。- 访问Web资源,响应回来会再次经过 f i l t e r filter filter,执行过滤器中的代码,到达浏览器端。
三、使用细节
1_生命周期
生命周期:指的是一个对象从生(创建)到死(销毁)的一个过程
// 初始化方法
public void init(FilterConfig config);
// 执行拦截方法
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain);
// 销毁方法
public void destroy();
-
创建
服务器启动项目加载,创建 f i l t e r filter filter对象,执行
init
方法(只执行一次) -
运行(过滤拦截)
用户访问被拦截目标资源时,执行
doFilter
方法 -
销毁
服务器关闭时,销毁 f i l t e r filter filter对象,执行
destroy
方法(只执行一次) -
补充:
过滤器一定是优先于
servlet
创建的,后于 S e r v l e t Servlet Servlet 销毁复习:
servlet
默认是第一次访问的时候创建
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/*
Filter的生命周期方法
1. init
web服务器启动时,此方法运行,就运行一次(早于Servlet)
2. doFilter
当请求经过过滤器,此方法就会运行(每经过一次,就运行一次) (早于servlet的service方法)
3. destroy
tomcat关闭, 此方法运行一次 (晚于servlet的destroy)
*/
@WebFilter("/MyFilterLife")
public class MyFilterLife implements Filter {
public MyFilterLife() {
System.out.println("MyFilterLife的构造方法被调用了..........");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilterLife init");
}
//当请求经过过滤器,此方法就会运行(每经过一次,就运行一次)
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("MyFilterLife doFilter");
//请求放行 (相当于请求转发)
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("MyFilterLife destroy");
}
}
总结:
- 过滤器是在
t
o
m
c
a
t
tomcat
tomcat启动的时候
t
o
m
c
a
t
tomcat
tomcat调用过滤器类中的无参构造方法创建对象,使用对象调用
init
方法,只会执行一次 - 当使用浏览器访问被过滤的资源的时候就会执行过滤器
F
i
l
t
e
r
Filter
Filter中的
doFilter
方法,访问一次就会执行一次doFilter
方法 - 当关闭
t
o
m
c
a
t
tomcat
tomcat服务器就会使用对象调用销毁方法
destroy
,只会执行一次 - 过滤器优先于访问的资源执行
2_拦截路径
在开发时,我们可以指定过滤器的拦截路径来定义拦截目标资源的范围
-
精准匹配
用户访问指定目标资源(
/demo01.html
)时,过滤器进行拦截 -
目录匹配
用户访问指定目录下(
/user/*
)所有资源时,过滤器进行拦截 -
后缀匹配
用户访问指定后缀名(
*.html
)的资源时,过滤器进行拦截,不能加/
-
匹配所有
用户访问该网站所有资源(
/*
)时,过滤器进行拦截
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//@WebFilter({"/xx","/yy"})
//@WebFilter("/xx") // 精准匹配
//@WebFilter("/user/*") // 目录匹配
//@WebFilter("*.do") // 后缀匹配
@WebFilter("/*") // 匹配所有
public class MyFilterPath 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("doFilter");
}
@Override
public void destroy() {
}
}
注意: 在过滤器中,如果多个过滤器过滤同一个资源,那么要执行所有满足条件的过滤器。
3_过滤器链
在一次请求中,若我们请求匹配到了多个 f i l t e r filter filter,通过请求就相当于把这些 f i l t e r filter filter串起来了,形成了过滤器链。
问题:如果多个过滤器都对相同路径进行匹配,执行顺序该是什么?
F i l t e r Filter Filter 默认是按照字母顺序执行的,如果过滤器名字第一个字母相同,再看过滤器名字的第二个字母,以此类推。从而形成一个执行链条。
前提:多个过滤器过滤同一个资源,并且多个过滤器是在同一包下。
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/apple")
public class AFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
System.out.println("AFilter....放行前");
chain.doFilter(request, response);
System.out.println("AFilter....放行后");
}
public void init(FilterConfig config) throws ServletException {
}
}
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/apple")
public class BFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
System.out.println("BFilter....放行前");
chain.doFilter(request, response);
System.out.println("BFilter....放行后");
}
public void init(FilterConfig config) throws ServletException {
}
}
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/apple")
public class AppleServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("AppleServlet....");
}
}
总结:
-
如果同一个包下,多个过滤器拦截同一个资源,那么按照过滤器类的名字字母升序来依次执行(针对注解开发)
AFilter ---- BFilter
-
执行完拦截资源后依次在按照相反顺序执行
-
如果是
xml
配置方式,那么按照从上往下配置的顺序执行
四、Listener
1_Listener概述
说明:
javaweb中的监听器是监听
S
e
r
v
l
e
t
C
o
n
t
e
x
t
ServletContext
ServletContext、
H
t
t
p
S
e
s
s
i
o
n
HttpSession
HttpSession、
H
t
t
p
S
e
r
v
l
e
t
R
e
q
u
e
s
t
HttpServletRequest
HttpServletRequest三个对象创建和销毁的,同时监听是哪个对象中数据的变化,就是监听属性的变化:setAttribute
、removeAttribute
。
S e r v l e t C o n t e x t ServletContext ServletContext 是在 t o m c a t tomcat tomcat启动创建,关闭 t o m c a t tomcat tomcat销毁
H t t p S e s s i o n HttpSession HttpSession 是在浏览器第一次访问执行
request.getSession()
创建,销毁时间:1)30min
2) 执行invalidate()
H t t p S e r v l e t R e q u e s t HttpServletRequest HttpServletRequest 是浏览器第一次访问创建,浏览器接收到服务器的响应就销毁
生活中的监听器
我们很多商场有摄像头,监视着客户的一举一动。如果客户有违法行为,商场可以采取相应的措施。
javaweb中的监听器
在我们的 java 程序中,有时也需要监视某些事情,一旦被监视的对象发生相应的变化,我们应该采取相应的操作。
监听 web 三大域对象: H t t p S e r v l e t R e q u e s t HttpServletRequest HttpServletRequest、 H t t p S e s s i o n HttpSession HttpSession、 S e r v l e t C o n t e x t ServletContext ServletContext (创建和销毁)
场景
历史访问次数、统计在线人数、系统启动时初始化配置信息
监听器的接口分类
事件源 | 监听器接口 | 时机 |
---|---|---|
ServletContext | ServletContextListener | 上下文域创建和销毁 |
ServletContext | ServletContextAttributeListener | 上下文域属性增删改的操作 |
HttpSession | HttpSessionListener | 会话域创建和销毁 |
HttpSession | HttpSessionAttributeListener | 会话域属性增删改的操作 |
HttpServletRequest | ServletRequestListener | 请求域创建和销毁 |
HttpServletRequest | ServletRequestAttributeListener | 请求域属性增删改的操作 |
2_监听器举例
监听的关键要素:
- 设置监听器的人
- 监听器
- 监听器目标: 被监听的对象
- 监听器工作: 被监听的对象执行某种行为,监听器就开始工作
web 中监听的关键要素:
-
雇佣人 : web程序开发者
-
监听器例子A
- 监听器A:ServletContextListener
- 目标 : 监听 S e r v l e t C o n t e x t ServletContext ServletContext 对象
- 执行 : 监听 S e r v l e t C o n t e x t ServletContext ServletContext 对象创建和销毁
-
监听器例子B
- 监听器A:HttpSessionListener
- 目标: H t t p S e s s i o n HttpSession HttpSession 对象
- 执行:监听 H t t p S e s s i o n HttpSession HttpSession 对象创建和销毁
-
监听器例子C
- 监听器A:HttpRequestListener
- 目标: H t t p R e q u e s t HttpRequest HttpRequest 对象
- 执行:监听 H t t p R e q u e s t HttpRequest HttpRequest 对象创建和销毁
-
监听器例子D
-
监听器A: ServletContextAttributeListener
-
目标 : S e r v l e t C o n t e x t ServletContext ServletContext对象
-
执行 : 监听 S e r v l e t C o n t e x t ServletContext ServletContext对象增删改数据
(add,remove)
当我们向 S e r v l e t C o n t e x t ServletContext ServletContext对象中添加数据
setAttribute()
和删除数据removeAttribute()
就会被ServletContextAttributeListener 监听器监听。
-
-
其他监听器
HttpSessionAttributeListener、HttpRequestAttributeListener
3_Listener开始
监听器在web开发中使用的比较少,见的机会就更少了,我们使用ServletContextListenner对监听器快速了解,因为这个监听器是监听器中使用率最高的一个,且监听器的使用方式都差不多。
我们使用这个监听器可以在项目启动和销毁的时候做一些事情,例如:在项目启动的时候加载配置文件。
步骤分析
-
创建一个普通类,实现ServletContextListenner
-
重写方法
-
监听 S e r v l e t C o n t e x t ServletContext ServletContext创建
-
监听 S e r v l e t C o n t e x t ServletContext ServletContext销毁
-
-
配置
-
web.xml
-
注解
-
① xml版本
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/*
设置监听器的人: 开发者
监听器 : MyListener
监听器目标 : ServletContext 对象
监听器的工作:
1). 当ServletContext 对象创建的时候就会执行contextInitialized方法
ServletContext是tomcat启动时就会创建 (早于Filter和Servlet)
2). 当ServletContext 对象销毁的时候就会执行contextDestroyed方法
ServletContext是tomcat关闭时销毁 (晚于Filter和Servlet)
这两个方法是事件驱动
*/
public class MyListener implements ServletContextListener {
//tomcat一启动,此方法就会运行
//运用场景: spring底层封装了一个ServletContextListener加载配置文件
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}
}
<!-- 配置监听器 -->
<listener>
<listener-class>com.example.sh.MyListener</listener-class>
</listener>
② 注解版本
//3.配置监听器
@WebListener
//1. 创建一个普通类,实现ServletContextListener
public class MyListener implements ServletContextListener {
//2.重写方法
//当上下文对象ServletContext创建就执行该方法
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
//当上下文对象ServletContext销毁就执行该方法
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}
}
4_案例:模拟spring框架
需求:可以在项目启动时读取配置文件,获取配置文件的名字。
步骤:
-
在
web.xml
进行配置文件的配置 -
创建自定义监听器类监听 S e r v l e t C o n t e x t ServletContext ServletContext的创建和销毁
-
在自定义监听器类中实现监听器的方法
-
实现的方法体中读取
web.xml
文件中关于当前项目配置文件的信息 -
输出结果
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
//2.创建自定义监听器类监听ServletContext的创建和销毁
//别忘记注册监听器
@WebListener
public class SpringContextListener implements ServletContextListener {
//3.在自定义监听器类中实现监听器的抽象方法
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
//4.实现的方法体中读取web.xml文件中关于当前项目配置文件的信息
//4.1根据事件类对象servletContextEvent调用方法获取ServletContext上下文对象
ServletContext servletContext = servletContextEvent.getServletContext();
//4.2使用上下文对象调用方法获取web.xml配置文件中的数据
/*
<context-param>
<param-name>jdbc</param-name>
<param-value>jdbc.properties</param-value>
</context-param>
*/
String str = servletContext.getInitParameter("jdbc");
System.out.println("str = " + str);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("释放资源....销毁。。。。");
}
}
<!--配置当前项目的配置文件信息-->
<!--1.在web.xml进行配置文件的配置-->
<context-param>
<param-name>jdbc</param-name>
<param-value>jdbc.properties</param-value>
</context-param>