一、过滤器(Filter)
1.1 过滤器基本特点
- 它会在一组资源(jsp、servlet、css、html等等)的前面执行
- 它可以让请求得到目标资源,也可以不让请求得到
- 过滤器有拦截请求的能力
1.2 如何编写一个过滤器
- 写一个类实现 Filter 接口
- 在 web.xml 中进行配置
1.2.1 Filter 生命周期的三大方法
过滤器的三个生命周期,以下为三个方法
import javax.servlet.*;
import java.io.IOException;
public class AFilter implements Filter {
/**
* 创建之后马上执行,用来初始化!
* Filter 会在服务器启动时创建
* */
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* 每次过滤都会执行
* */
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
/**
* 销毁之前执行,用来做对非内存资源进行释放
* */
@Override
public void destroy() {
}
}
Filter 是单例的
1.2.2 web.xml 配置过滤器
这里我们拦截 AServlet 的访问
<?xml version="1.0" encoding="UTF-8"?>
<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">
<filter>
<filter-name>AFilter</filter-name>
<filter-class>cn.study.web.filter.AFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AFilter</filter-name>
<!-- /* 访问任何资源都会经过过滤器
/web/*
/AServlet 拦截指定的内容
-->
<url-pattern>/AServlet</url-pattern>
</filter-mapping>
</web-app>
1.2.3 run
成功被拦截
1.3 Filter 使用场景
借助 Filter 的特性,在 Tomcat 服务器运行时,就初始化的特性,我们可以设置全局的编码格式
- 在 web.xml 中配置过滤器以及参数
```xml
<?xml version=”1.0” encoding=”UTF-8”?>
2. 编写 CharacterEncodingFilter
```java
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 初始化编码
@WebFilter(filterName = "CharacterEncodingServlet")
public class CharacterEncodingFilter implements Filter {
private String encoding;
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
response.setContentType("text/html;charset="+encoding);
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
encoding = config.getInitParameter("encoding"); // 获取 xml 中的参数的值
}
}
二、理解 Filter 参数
2.1 FilterConfig
- 获取初始化参数:getInitParameter()
- 获取过滤器名称:getFilterName()
- 获取 application:getServletContext()
2.2 FilterChain
- doFilter(ServletRequest, ServletResponse) 放行,相当于执行了目标的 servlet 的 service 方法!
修改 doFilter 的内容
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("拦截你"); filterChain.doFilter(servletRequest, servletResponse); // 放行 System.out.println("可以访问啦"); }
访问 AServlet
- 然后可以看到 AServlet 可以执行了
三、多过滤器拦截
FilterChain # doFilter 方法: 执行目标资源,或者执行下一个过滤器! 如果没有下一个过滤器,那么执行的是目标资源,如果有,那么执行下一个过滤器
3.1 过滤器的四种拦截方式
- 请求 DISPATCHER
- 转发 FORWARD
- 包含 INCLUDE
- 错误 ERROR
在 filter-mapping 中进行配置
<filter>
<filter-name>AFilter</filter-name>
<filter-class>cn.study.web.filter.AFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AFilter</filter-name>
<!-- /* 访问任何资源都会经过过滤器
/web/*
/AServlet 拦截指定的内容
-->
<url-pattern>/AServlet</url-pattern>
<dispatcher>REQUEST</dispatcher> <!-- 默认的! -->
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
3.2 多个过滤器的执行顺序
在 filter-mapping 中的配置顺序,决定了执行顺序
<filter>
<filter-name>AFilter</filter-name>
<filter-class>cn.study.web.filter.AFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AFilter</filter-name>
<url-pattern>/AServlet</url-pattern>
</filter-mapping>
<filter>
<filter-name>BFilter</filter-name>
<filter-class>cn.study.web.filter.BFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>BFilter</filter-name>
<url-pattern>/BServlet</url-pattern>
</filter-mapping>
四、filter 小结
4.1 过滤器的应用场景
- 执行目标资源之前做预处理工作,例如设置编码,这种试通常都会放行,只是在目标资源执行之前做一些准备工作;。(几乎所有的 Servlet 都要设置 request.setCharcterEncoding() 可以把它放到一个 Filter中)
- 通过条件判断是否放行,例如校验当前用户是否已经登录,或者用户IP是否已经被禁用; +
- 在目标资源执行后,做一些后续的特殊处理工作,例如把目标资源输出的数据进行处理;。(回程拦截)
4.2 filter 三个方法
- void init(FilterConfig filterConfig) Tomcat 启动时被调用
- void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 每次请求时都调用该方法
- void destroy() Tomcat 关闭时调用
4.3 四种拦截方式
- 拦截请求 DISPATCHER
- 拦截转发 FORWARD
- 拦截包含 INCLUDE
- 拦截错误 ERROR
4.4 两个 filter 参数
- filterConfig(对比 ServletConfig)
- filterChain (放行)
五、Filter 案例
5.1 分 ip 统计网站访问次数
ip | count |
---|---|
192.168.1.112 | 5 |
192.168.1.113 | 52 |
统计工作要在所有资源之前执行,那么就可以放到 Filter 中。
这个过滤器不打算做拦截操作,因为作为统计使用
用什么东西统计数据:Map
整个网站只需要一个 Map 即可!
Map 什么时候创建(使用 ServletContextListener,在服务器启动时完成创建,并只在到 ServletContext中), Map 保存到哪里(Map 保存到 ServletContext)
- Map 需要在 Filter 中保存数据
- Map 需要在页面中使用,打印 Map中的数据
六、监听器(Listener)
监听器
- 它是一个接口,内容由我们来实现
- 他需要注册
- 监听器中的方法,会在特殊事件发生时调用
观察者
- 事件源(小偷)
- 事件(偷东西)
- 监听器(警察,监听器的方法,抓捕)
6.1 监听器概述
6.1.1 事件源:三大域
ServletContext
- 生命周期监听:ServletContextListener,它有两个方法,
- 一个在出生时调用:void contextInitialized(ServletContextEvent sce) 创建 ServletContext 时
- 一个在死亡时调用:void contextDestroyed(ServletContextEvent sce) 销毁 ServletContext 时
- 属性监听:ServletContextAttribute:它有三个方法:
- 一个在添加属性时调用: void attributeAdded(SenvletContextAttributeEvent event): 添加属性时;
- 一个在替换属性时调用: void attributeReplaced(SevletContextAttributeEvent event): 替换属性时;
- 最后一个在移除属性时调用: void atributeRemoved(ServletContexttributeEvent event): 移除属性时;
- 生命周期监听:ServletContextListener,它有两个方法,
HTTPSession
- 生命周期监听:HTTPSessiontListener,它有两个方法,
- 一个在出生时调用:void sessionCreated(HttpSessionEvent se) 创建 session 时
- 一个在死亡时调用:void sessionDestroyed(HttpSessionEvent se) 销毁 session 时
- 属性监听:HTTPSessiontAttribute:它有三个方法:
- 一个在添加属性时调用: void attributeAdded(HttpSessionBindingEvent event): 添加属性时;
- 一个在替换属性时调用: void atributeReplaced(HittpSessionBindingEvent event):替换属性时
- 最后一个在移除属性时调用: void attributeRemoved(HttpSessionBindingEvent event):移除属性时
- 生命周期监听:HTTPSessiontListener,它有两个方法,
ServletRequest
- 生命周期监听:ServletRequestListener,它有两个方法,
- 一个在出生时调用:void requestInitialized(ServletRequestEvent sre)
- 一个在死亡时调用:void requestDestroyed(ServletRequestEvent sre)
- 属性监听:ServletRequestAttribute:它有三个方法:
- 一个在添加属性时调用: vold attributeAdded(ServletRequestAttributeEvent sree):添加属性时
- 一个在替换属性时调用: void attributeReplaced(ServletRequestAttributeEvent srae):替换属性时
- 最后一个在移除属性时调用: void attributeRemoved(ServletRequestAttributeEvent srae): 移除属性时
- 生命周期监听:ServletRequestListener,它有两个方法,
6.1.2 JavaWeb 中完成编写监听器:
- 写一个监听器类,要求必须实现某个监听器的接口
- 注册:是在 web.xml 中实现注册
实现 ServletContextListener 接口
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* ServletContext 生死监听
可以在监听器中存放一些 Tomcat启动时要完成的代码
* */
public class AListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("我是监听器 AListener"); // 出生了
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println(" 我要死亡了");
}
}
注册
<?xml version="1.0" encoding="UTF-8"?>
<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">
<listener>
<listener-class>cn.study.web.listeber.AListener</listener-class>
</listener>
</web-app>
监听器触发
6.1.3 事件对象
- ServletContextEvent:
- ServletContext getServletContext()
- HttpSessionEvent::
- HttpSession getSession()
- ServletRequestEvent
- ServletContext getServletContext()
- ServletRequest getServletRequestEvent()
- SenvletContextAttributeEvent
- ServletContext getServletContext()
- String getName() 获取属性名
- Object getValue() 获取属性值
- HttpSessionBindingEvent
- ServletRequestAttributeEvent
6.1.4 感知监听 (与 HTTPSession 相关)
- 它用来添加到 javaBean上,而不是添加到三大域上
- 这两个监听器无需在 web.xml 中注册
HttpSessionBindingListener:添加到 JavaBean上,javaBean 就知道自己是否添加到 session 中了
- 编写 User 并实现 HttpSessionBindingListener 接口 ```java import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener;
public class User implements HttpSessionBindingListener { private String username; private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
// getter 和 setter 省略
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("啊~, session 添加了我");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("哇, session 抛弃了我");
}
}
2. 分别编写 add.jsp 和 remove.jsp
add.jsp
```jsx
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="cn.study.web.listener.User" %>
<html>
<head>
<title>添加 session</title>
</head>
<body>
<%
User user = new User("user","111");
session.setAttribute("user",user);
%>
</body>
</html>
remove.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>remove.jsp</title>
</head>
<body>
<h1>删除 session</h1>
<%
session.removeAttribute("user");
%>
</body>
</html>
- 然后运行 Tomcat 分别运行 add.jsp 和 remove.jsp