热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaWeb笔记(8):Filter过滤器/ThreadLocal

目录1、Filter是什么过滤器2、Filter的工作流程图:3、Filter过滤器的使用步骤:4、Filter的生命周期5、FilterCo

目录

1、Filter 是什么过滤器

2、Filter 的工作流程图:

3、Filter 过滤器的使用步骤:

4、Filter 的生命周期

5、FilterConfig 类

6、FilterChain 过滤器链

7、Filter 的拦截路径

8、ThreadLocal 的使用

9、使用 Filter 和 ThreadLocal 组合管理事务




1、Filter 是什么过滤器


  • 1、 JavaWeb 三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器。
  • 2、Filter 过滤器它是 JavaEE 的规范。也就是接口
  • 3、Filter 过滤器作用是: 拦截请求,过滤响应

拦截请求常见的应用场景有:


  • ①、权限检查
  • ②、日记操作
  • ③、事务管理
  • ……等等

2、Filter 的工作流程图:

Filter实现原理:

在这里插入图片描述


3、Filter 过滤器的使用步骤:


  • 1、编写一个类去实现 Filter 接口
  • 2、实现过滤方法 doFilter()
  • 3、到 web.xml 中去配置 Filter 的拦截路径

Filter 的代码:

public class AdminFilter implements Filter {/*** doFilter 方法,专门用于拦截请求。可以做权限检查*/@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;HttpSession session = httpServletRequest.getSession();Object user = session.getAttribute("user");// 如果等于 null ,说明还没有登录if (user == null) {servletRequest.getRequestDispatcher("/admin/a.html").forward(servletRequest,servletResponse);return;} else {// 让程序继续往下访问用户的目标资源filterChain.doFilter(servletRequest,servletResponse);}}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void destroy() {}
}

web.xml 中的配置:

AdminFiltercom.crane.filter.AdminFilterAdminFilter/admin/*

a.html 页面 == 登录表单

用户名:
密 码:

LoginServlet 程序

public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 解决中文乱码问题req.setCharacterEncoding("UTF-8");resp.setContentType("text/html; charset=UTF-8");String username = req.getParameter("username");String password = req.getParameter("password");if ("Tommey周".equals(username) && "123456".equals(password)) {req.getSession().setAttribute("user",username);resp.getWriter().write("登录成功");} else {req.getRequestDispatcher("/admin/a.html").forward(req,resp);}}
}

web.xml中filter用于过滤/admin/*中的内容。 

过程:

1、访问http://localhost:8080/WebServlet_war_exploded/admin/a.html

2、提示先登录

 3、登陆成功后

4、访 问http://localhost:8080/WebServlet_war_exploded/admin/a.html。 正常


4、Filter 的生命周期

Filter 的生命周期包含几个方法


  • 1、构造器方法
  • 2、init 初始化方法
    • 第 1,2 步,在 web 工程启动的时候执行(Filter 已经创建)
  • 3、doFilter 过滤方法
    • 第 3 步,每次拦截到请求,就会执行
  • 4、destroy 销毁
    • 第 4 步,停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤器)

5、FilterConfig 类

FilterConfig 是 Filter 过滤器的配置文件类

Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。

FilterConfig 类的作用是获取 filter 过滤器的配置内容


  • 1、获取 Filter 的名称 filter-name 的内容
  • 2、获取在 Filter 中配置的 init-param 初始化参数
  • 3、获取 ServletContext 对象

public class AdminFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("Filter 的 的 init(FilterConfig filterConfig) 初始化");// 1 、获取 Filter 的名称 filter-name 的内容System.out.println("filter-name 的值是:" + filterConfig.getFilterName());// 2 、获取在 web.xml 中配置的 init-param 初始化参数System.out.println(" 初始化参数 username 的值是 :" + filterConfig.getInitParameter("username"));System.out.println(" 初始化参数 url 的值是:" + filterConfig.getInitParameter("url"));// 3 、获取 ServletContext 对象System.out.println(filterConfig.getServletContext());}

web.xml代码:


AdminFiltercom.AdminFilterusernamerooturljdbc:mysql://localhost3306/test


6、FilterChain 过滤器链


  • Filter ========》过滤器
  • Chain =======》链,链条
  • FilterChain ===》 就是过滤器链(多个过滤器如何一起工作) 

多个Filter过滤器执行的特点:


  • 1、所有Filter和目标资源默认执行在同一个线程中
  • 2、多个Filter共同执行的时候,它们都使用同一个Request对象

FilterChain.doFilter()方法的作用


  • 1、执行下一个Filter过滤器(如果有Filter)
  • 2、执行目标资源(没有Filter)

在多个Filter过滤器执行的时候,他们执行的优先顺序是由他们在web.xml中从上到下配置的顺序决定的。

案例:

public class Filter1 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("filter1 前置");System.out.println("filter1参数:" + servletRequest.getParameter("username"));System.out.println("filter1线程:" + Thread.currentThread().getName());filterChain.doFilter(servletRequest, servletResponse);System.out.println("filter1线程:" + Thread.currentThread().getName());System.out.println("filter1 后置");}@Overridepublic void destroy() {}
}public class Filter2 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("filter2 前置");System.out.println("filter2参数:" + servletRequest.getParameter("username"));System.out.println("filter2线程:" + Thread.currentThread().getName());filterChain.doFilter(servletRequest, servletResponse);System.out.println("filter2线程:" + Thread.currentThread().getName());System.out.println("filter2 后置");}@Overridepublic void destroy() {}
}


Filter1com.crane.filter.Filter1Filter1/target.jspFilter2com.crane.filter.Filter2 Filter2/target.jsp

<%&#64; page language&#61;"java" contentType&#61;"text/html" pageEncoding&#61;"GBK"%>



filter执行

<%System.out.println("target.jsp页面执行了");System.out.println("target.jsp页面 线程&#xff1a;" &#43; Thread.currentThread().getName());System.out.println("target.jsp页面参数&#xff1a;" &#43; request.getParameter("username"));
%>

http://localhost:8080/WebServlet_war_exploded/target.jsp?username&#61;li

filter1 前置
filter1线程&#xff1a;http-nio-8080-exec-7
filter2 前置
filter2线程&#xff1a;http-nio-8080-exec-7
target.jsp页面执行了
target.jsp页面 线程&#xff1a;http-nio-8080-exec-7
filter2线程&#xff1a;http-nio-8080-exec-7
filter2 后置
filter1线程&#xff1a;http-nio-8080-exec-7
filter1 后置



7、Filter 的拦截路径

1、精确匹配


  • /target.jsp
  • 配置的路径表示请求地址必须为&#xff1a;http://ip:port/工程路径/target.jsp

2、目录匹配


  • /admin/*
  • 配置的路径表示请求地址必须为&#xff1a;http://ip:port/工程路径/admin/*

3、后缀名匹配


  • *.html&#xff0c;配置的路径表示请求地址必须以.html 结尾才会拦截到
  • *.do&#xff0c;配置的路径表示请求地址必须以.do 结尾才会拦截到
  • *.action&#xff0c;配置的路径表示请求地址必须以.action 结尾才会拦截到

Filter 过滤器它只关心请求的地址是否匹配&#xff0c;不关心请求的资源是否存在


8、ThreadLocal 的使用


  • ThreadLocal 的作用&#xff0c;它可以解决多线程的数据安全问题。
  • ThreadLocal 它可以给当前线程关联一个数据&#xff08;可以是普通变量&#xff0c;可以是对象&#xff0c;也可以是数组&#xff0c;集合&#xff09;
  • ThreadLocal 的特点&#xff1a;
    • 1、ThreadLocal 可以为当前线程关联一个数据&#xff08;它可以像 Map 一样存取数据&#xff0c;key 为当前线程&#xff09;。
    • 2、每一个 ThreadLocal 对象&#xff0c;只能为当前线程关联一个数据&#xff0c;如果要为当前线程关联多个数据&#xff0c;就需要使用多个ThreadLocal 对象实例。
    • 3、每个 ThreadLocal 对象实例定义的时候&#xff0c;一般都是 static 类型
    • 4、ThreadLocal 中保存数据&#xff0c;在线程销毁后。会由 JVM 虚拟自动释放

示例代码&#xff1a;ThreadLocalTest 类

public class ThreadLocalTest {// public static Map data &#61; new Hashtable();public static ThreadLocal threadLocal &#61; new ThreadLocal();private static Random random &#61; new Random();public static class Task implements Runnable {&#64;Overridepublic void run() {// 在 Run 方法中&#xff0c;随机生成一个变量&#xff08;线程要关联的数据&#xff09;&#xff0c;然后以当前线程名为 key 保存到 map 中Integer i &#61; random.nextInt(1000);// 获取当前线程名String name &#61; Thread.currentThread().getName();System.out.println(" 线程[" &#43; name &#43; "] 生成的随机数是&#xff1a;" &#43; i);// data.put(name,i);threadLocal.set(i);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}new OrderService().createOrder();// 在 Run 方法结束之前&#xff0c;以当前线程名获取出数据并打印。查看是否可以取出操作// Object o &#61; data.get(name);Object o &#61; threadLocal.get();System.out.println(" 在线程[" &#43; name &#43; "] 快结束时取出关联的数据是&#xff1a;" &#43; o);}}public static void main(String[] args) {for (int i &#61; 0; i <3; i&#43;&#43;) {new Thread(new Task()).start();}}
}

OrderService 类&#xff1a;

public class OrderService {public void createOrder() {String name &#61; Thread.currentThread().getName();System.out.println("OrderService 当前线程[" &#43; name &#43; "] 中保存的数据是&#xff1a;" &#43;ThreadLocalTest.threadLocal.get());new OrderDao().saveOrder();}
}

OrderDao 类&#xff1a;

public class OrderDao {public void saveOrder() {String name &#61; Thread.currentThread().getName();System.out.println("OrderDao 当前线程[" &#43; name &#43; "] 中保存的数据是&#xff1a;" &#43;ThreadLocalTest.threadLocal.get());}
}

 线程[Thread-1] 生成的随机数是&#xff1a;160
 线程[Thread-0] 生成的随机数是&#xff1a;445
 线程[Thread-2] 生成的随机数是&#xff1a;364
 在线程[Thread-0] 快结束时取出关联的数据是&#xff1a;445
 在线程[Thread-2] 快结束时取出关联的数据是&#xff1a;364
 在线程[Thread-1] 快结束时取出关联的数据是&#xff1a;160



9、使用 Filter 和 ThreadLocal 组合管理事务

图书系统中&#xff1a;选择购物车在结算时&#xff0c;在一个方法中实现了保存订单order、保存订单项order_item、更新库存和销量的操作&#xff0c;该操作必须保证事务。

如果没有事务&#xff0c;在保存订单后出错时&#xff0c;会出现以下落库问题&#xff1a;

此时订单表保存成功&#xff0c;订单项表 为空 &#xff0c;则表示用户付了该订单&#xff0c;而商家不知道该发货的商品是哪个。故需要事务。

1、JDBC事务

使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完成&#xff1a;


  • 原理分析图

注意&#xff1a;所有操作在try...catch时需要抛异常到回滚处&#xff0c;不能只try.catch不抛。


  • JdbcUtils工具类的修改&#xff1a;

public class JdbcUtils {private static DruidDataSource dataSource;private static ThreadLocal conns &#61; new ThreadLocal();static {try {Properties properties &#61; new Properties();// 读取 jdbc.properties 属性配置文件InputStream inputStream &#61; JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");// 从流中加载数据properties.load(inputStream);// 创建 数据库连接 池dataSource &#61; (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}/*** 获取数据库连接池中的连接** &#64;return 如果返回 null, 说明获取连接失败
有值就是获取连接成功*/public static Connection getConnection() {Connection conn &#61; conns.get();if (conn &#61;&#61; null) {try {conn &#61; dataSource.getConnection();// 从数据库连接池中获取连接conns.set(conn); // 保存到 ThreadLocal 对象中&#xff0c;供后面的 jdbc 操作使用conn.setAutoCommit(false); // 设置为手动管理事务} catch (SQLException e) {e.printStackTrace();}}return conn;}/*** 提交事务&#xff0c;并关闭释放连接*/public static void commitAndClose() {Connection connection &#61; conns.get();if (connection !&#61; null) { // 如果不等于 null &#xff0c;说明 之前使用过连接&#xff0c;操作过数据库try {connection.commit(); // 提交 事务} catch (SQLException e) {e.printStackTrace();} finally {try {connection.close(); // 关闭连接&#xff0c;资源资源} catch (SQLException e) {e.printStackTrace();}}}// 一定要执行 remove 操作&#xff0c;否则就会出错。&#xff08;因为 Tomcat 服务器底层使用了线程池技术&#xff09;conns.remove();}/*** 回滚事务&#xff0c;并关闭释放连接*/public static void rollbackAndClose() {Connection connection &#61; conns.get();if (connection !&#61; null) { // 如果不等于 null &#xff0c;说明 之前使用过连接&#xff0c;操作过数据库try {connection.rollback();// 回滚事务} catch (SQLException e) {e.printStackTrace();} finally {try {connection.close(); // 关闭连接&#xff0c;资源资源} catch (SQLException e) {e.printStackTrace();}}}// 一定要执行 remove 操作&#xff0c;否则就会出错。&#xff08;因为 Tomcat 服务器底层使用了线程池技术&#xff09;conns.remove();}/*** 关闭连接&#xff0c;放回数据库连接池* &#64;param conn*/public static void close(Connection conn) {if (conn !&#61; null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}
}

2、使用 Filter 过滤器统一给所有的 Service 方法都加上 try-catch &#xff0c;来进行实现的 管理

  • 原理分析图&#xff1a;

在这里插入图片描述 Filter 类代码&#xff1a; 

public class TransactionFilter implements Filter {&#64;Overridepublic void init(FilterConfig filterConfig) throws ServletException {}&#64;Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {try {filterChain.doFilter(servletRequest,servletResponse);JdbcUtils.commitAndClose();// 提交事务} catch (Exception e) {JdbcUtils.rollbackAndClose();// 回滚事务e.printStackTrace();}}&#64;Overridepublic void destroy() {}
}

web.xml

TransactionFiltercom.TransactionFilter

TransactionFilter/*


  • 把 BaseServlet 中的异常往外抛给 Filter 过滤器

public abstract class BaseServlet extends HttpServlet {&#64;Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 解决 post 请求中文乱码问题// 一定要在获取请求参数之前调用才有效req.setCharacterEncoding("UTF-8");String action &#61; req.getParameter("action");try {// 获取 action 业务鉴别字符串&#xff0c;获取相应的业务 方法反射对象Method method &#61; this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);System.out.println(method);// 调用目标业务 方法method.invoke(this, req, resp);} catch (Exception e) {e.printStackTrace();throw new RuntimeException(e);// 把异常抛给 Filter 过滤器}}
}

3将所有异常都统一交给 Tomcat &#xff0c;让 Tomcat 展示友好的错误信息页面

  • 在 web.xml 中我们可以通过错误页面配置来进行管理


500/pages/error/error500.jsp


404/pages/error/error404.jsp


推荐阅读
  • Skywalking系列博客1安装单机版 Skywalking的快速安装方法
    本文介绍了如何快速安装单机版的Skywalking,包括下载、环境需求和端口检查等步骤。同时提供了百度盘下载地址和查询端口是否被占用的命令。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
author-avatar
小丽丽很可爱
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有