目录
1、Filter 是什么过滤器
2、Filter 的工作流程图:
3、Filter 过滤器的使用步骤:
4、Filter 的生命周期
5、FilterConfig 类
6、FilterChain 过滤器链
7、Filter 的拦截路径
8、ThreadLocal 的使用
9、使用 Filter 和 ThreadLocal 组合管理事务
拦截请求常见的应用场景有:
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 中的配置:
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。 正常
Filter 的生命周期包含几个方法
FilterConfig 是 Filter 过滤器的配置文件类。
Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。
FilterConfig 类的作用是获取 filter 过滤器的配置内容
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代码:
多个Filter过滤器执行的特点:
FilterChain.doFilter()方法的作用
在多个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() {}
}
<%&#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 后置
1、精确匹配
2、目录匹配
3、后缀名匹配
Filter 过滤器它只关心请求的地址是否匹配&#xff0c;不关心请求的资源是否存在。
示例代码&#xff1a;ThreadLocalTest 类
public class ThreadLocalTest {// public static Map
}
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
图书系统中&#xff1a;选择购物车在结算时&#xff0c;在一个方法中实现了保存订单order、保存订单项order_item、更新库存和销量的操作&#xff0c;该操作必须保证事务。
如果没有事务&#xff0c;在保存订单后出错时&#xff0c;会出现以下落库问题&#xff1a;
此时订单表保存成功&#xff0c;订单项表 为空 &#xff0c;则表示用户付了该订单&#xff0c;而商家不知道该发货的商品是哪个。故需要事务。
1、JDBC事务
使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完成&#xff1a;
注意&#xff1a;所有操作在try...catch时需要抛异常到回滚处&#xff0c;不能只try.catch不抛。
public class JdbcUtils {private static DruidDataSource dataSource;private static ThreadLocal
有值就是获取连接成功*/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();}}}
}
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
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 过滤器}}
}