热门标签 | 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


推荐阅读
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • Scala 实现 UTF-8 编码属性文件读取与克隆
    本文介绍如何使用 Scala 以 UTF-8 编码方式读取属性文件,并实现属性文件的克隆功能。通过这种方式,可以确保配置文件在多线程环境下的一致性和高效性。 ... [详细]
  • PHP 过滤器详解
    本文深入探讨了 PHP 中的过滤器机制,包括常见的 $_SERVER 变量、filter_has_var() 函数、filter_id() 函数、filter_input() 函数及其数组形式、filter_list() 函数以及 filter_var() 和其数组形式。同时,详细介绍了各种过滤器的用途和用法。 ... [详细]
  • 本文详细探讨了HTML表单中GET和POST请求的区别,包括它们的工作原理、数据传输方式、安全性及适用场景。同时,通过实例展示了如何在Servlet中处理这两种请求。 ... [详细]
  • 尽管使用TensorFlow和PyTorch等成熟框架可以显著降低实现递归神经网络(RNN)的门槛,但对于初学者来说,理解其底层原理至关重要。本文将引导您使用NumPy从头构建一个用于自然语言处理(NLP)的RNN模型。 ... [详细]
  • 深入理解Java泛型:JDK 5的新特性
    本文详细介绍了Java泛型的概念及其在JDK 5中的应用,通过具体代码示例解释了泛型的引入、作用和优势。同时,探讨了泛型类、泛型方法和泛型接口的实现,并深入讲解了通配符的使用。 ... [详细]
  • ASP.NET MVC中Area机制的实现与优化
    本文探讨了在ASP.NET MVC框架中,如何通过Area机制有效地组织和管理大规模应用程序的不同功能模块。通过合理的文件夹结构和命名规则,开发人员可以更高效地管理和扩展项目。 ... [详细]
  • 本文详细探讨了JDBC(Java数据库连接)的内部机制,重点分析其作为服务提供者接口(SPI)框架的应用。通过类图和代码示例,展示了JDBC如何注册驱动程序、建立数据库连接以及执行SQL查询的过程。 ... [详细]
  • 使用GDI的一些AIP函数我们可以轻易的绘制出简 ... [详细]
  • 本文深入探讨了HTTP请求和响应对象的使用,详细介绍了如何通过响应对象向客户端发送数据、处理中文乱码问题以及常见的HTTP状态码。此外,还涵盖了文件下载、请求重定向、请求转发等高级功能。 ... [详细]
  • 本文介绍如何在Spring Boot项目中集成Redis,并通过具体案例展示其配置和使用方法。包括添加依赖、配置连接信息、自定义序列化方式以及实现仓储接口。 ... [详细]
  • 我有一个SpringRestController,它处理API调用的版本1。继承在SpringRestControllerpackagerest.v1;RestCon ... [详细]
  • 深入探讨CPU虚拟化与KVM内存管理
    本文详细介绍了现代服务器架构中的CPU虚拟化技术,包括SMP、NUMA和MPP三种多处理器结构,并深入探讨了KVM的内存虚拟化机制。通过对比不同架构的特点和应用场景,帮助读者理解如何选择最适合的架构以优化性能。 ... [详细]
  • 配置多VLAN环境下的透明SQUID代理
    本文介绍如何在包含多个VLAN的网络环境中配置SQUID作为透明网关。网络拓扑包括Cisco 3750交换机、PANABIT防火墙和SQUID服务器,所有设备均部署在ESXi虚拟化平台上。 ... [详细]
  • Java项目分层架构设计与实践
    本文探讨了Java项目中应用分层的最佳实践,不仅介绍了常见的三层架构(Controller、Service、DAO),还深入分析了各层的职责划分及优化建议。通过合理的分层设计,可以提高代码的可维护性、扩展性和团队协作效率。 ... [详细]
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社区 版权所有