前言:本文章是Java Web后端技术系列的第六篇,主要介绍Filter和Listener的概念和使用。接下来将持续更新,感兴趣的小伙伴请持续关注。本人也是初接触Java Web方面的知识,尚有很多不足,如有错误还望指正!
Web的三大组件
- Servlet:控制器(点击查看servlet文章)
- Filter:过滤器
- Listener:监听器
Filter
介绍
当用户访问服务器资源时,过滤器将请求拦截下来,完成一些通用的操作
应用场景:登录验证、统一编码处理、敏感字符过滤
使用
基本使用
步骤
- 编写java类继承Filter接口
a. 在这个类中定义拦截的规则 - 配置web.xml文件
a. 设置要拦截的是对哪一个servlet的请求
web.xml
文件中的配置信息如下
<filter>
<!--拦截器的名字-->
<filter-name>quickFilter</filter-name>
<!--拦截器类-->
<filter-class>com.filter.quickFilter</filter-class>
</filter>
<filter-mapping>
<!--拦截器名字-->
<filter-name>quickFilter</filter-name>
<!--下面的地址是浏览器访问这个servlet的地址,常用的是“/*”,表示对所有访问拦截-->
<url-pattern>/TargetServlet</url-pattern>
</filter-mapping>
例子
servlet类
@WebServlet("/TargetServlet")
public class TargetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("访问到目标资源");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("访问到目标资源");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
拦截器类
public class quickFilter implements Filter {
//初始化方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* 过滤方法
* @param servletRequest 请求对象
* @param servletResponse 响应对象
* @param filterChain 过滤器链(是否放行)
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("quickFilter拦截了请求");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
//销毁方法
@Override
public void destroy() {
}
}
web.xml
配置文件
<filter>
<filter-name>quickFilter</filter-name>
<filter-class>com.filter.quickFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>quickFilter</filter-name>
<!--下面的地址是浏览器访问这个servlet的地址,常用的是“/*”,表示对所有访问拦截-->
<url-pattern>/TargetServlet</url-pattern>
</filter-mapping>
拦截路径的设置
- 精准匹配
- 用户访问指定目标资源(/targetServlet)时,过滤器进行拦截
- 目录匹配
- 用户访问指定目录下(/user/*)所有资源时,过滤器进行拦截
- 后缀匹配
- 用户访问指定后缀名(*.html)的资源时,过滤器进行拦截
- 匹配所有
- 用户访问该网站所有资源(/*)时,过滤器进行拦截
过滤器链
如果用户访问目标资源 /targetServlet
时,需要经过 FilterA、FilterB
那么过滤器链执行顺序 (先进后出)
- 用户发送请求
- FilterA拦截,放行
- FilterB拦截,放行
- 执行目标资源 show.jsp
- FilterB增强响应
- FilterA增强响应
- 封装响应消息格式,返回到浏览器
过滤器链中执行的先后问题:配置文件 <filter-mapping>
,谁先声明,谁先执行
案例
使用filter统一编解码问题
思路
写一个过滤器对所有请求进行拦截,在过滤器中统一编解码的方式。
实现
要访问的servlet
public class wordsServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取表单中的信息
String content = req.getParameter("content");
//2.将请求参数的值输出到浏览器
resp.getWriter().write("留言内容:"+content);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
过滤器
public class encodeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//在过滤器中统一编解码方式
//向下转型
HttpServletRequest hsr = (HttpServletRequest) servletRequest;
HttpServletResponse hsrp = (HttpServletResponse) servletResponse;
//判断是否post请求
//因为get请求服务器的解码方式是utf-8,与浏览器一致
if (hsr.getMethod().equalsIgnoreCase("post")) {
hsr.setCharacterEncoding("UTF-8");
}
hsrp.setContentType("text/html;charset=utf-8");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
前端代码
<%--
Created by IntelliJ IDEA.
User: Leo
Date: 2020/9/20
Time: 9:38
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h3>留言板</h3>
<form action="/filter_demo/wordsServlet" method="post">
<textarea name="content" id="text" cols="30" rows="10"></textarea>
<input type="submit" value="提交留言"/><br/>
</form>
</body>
</html>
web.xml
文件中的配置
<servlet>
<servlet-name>wordsServlet</servlet-name>
<servlet-class>com.servlet.wordsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>wordsServlet</servlet-name>
<url-pattern>/wordsServlet</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encodeFilter</filter-name>
<filter-class>com.filter.encodeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
原理
浏览器向tomcat服务器发起请求,tomcat服务器会生成两个对象:request和response,将它们传给filter,执行filter里面的逻辑决定是否放行,如果放行就把这两个对象传给目标servlet。响应也会进行拦截。
相当于在浏览器和目标servlet之间加了一道网,它们之间的通信(请求或响应)都要经过这道网的审查,进来可能会被拦截下来,但出去不会被拦截掉。
生命周期
- 创建
服务器启动项目加载,创建filter对象,执行init方法(只执行一次)
初始化方法public void init(FilterConfig config);
- 运行(过滤拦截)
用户访问被拦截目标资源时,执行doFilter方法
执行拦截方法public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain);
- 销毁
服务器关闭项目卸载时,销毁filter对象,执行destroy方法(只执行一次)
销毁方法public void destroy();
- 补充:过滤器一定是优先于servlet创建的
总结
Listener
介绍
在我们的java程序中,有时也需要监视某些事情,一旦被监听的对象发生相应的变化,我们应该采取相 应的操作。
监听web三大域对象:HttpServletRequest、HttpSession、ServletContext
通过监听器监听三大域对象它们的创建和销毁
场景:历史访问次数、统计在线人数、系统启动时初始化配置信息
使用
以ServletContextListener
为例
步骤
- 创建一个类实现
ServletContextListenner
接口 - 实现
ServletContextListenner
的contextInitialized
和contextDestroyed
方法。 - 给这个类在xml中配置
实现
public class MyServletContextListenner1 implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("服务器启动,servletContext被创建了");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("服务器停止,servletContext被销毁了");
}
}
<listener>
<listener-class>com.itheima.listenner.MyServletContextListenner1</listenerclass>
</listener>
同理:使用如下接口以相同的方式也可以监听到session对象和request对象的创建和销毁
HttpSessionListener:监听Httpsession域的创建于销毁的监听器
ServletRequestListener:监听ServletRequest域的创建于销毁的监听器