一、Filter
1、概述
Filter 过滤器它是 JavaWeb 的三大组件之一。三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器。
Filter 过滤器它是 JavaEE 的规范。也就是接口。
Filter 过滤器它的作用是:拦截请求,过滤响应。
拦截请求常见的应用场景有:
权限检查
日记操作
事务管理
2、Filter使用
Filter 过滤器的使用步骤:
编写一个类去实现 Filter 接口
实现过滤方法 doFilter()
到 web.xml 中去配置 Filter 的拦截路径
AdminFilter.java:
doFilter方法中的filterChain.doFilter()
方法必须要写,可以让未被拦截的请求继续向下执行,若不写该方法,则不被拦截的请求也无法访问到目标资源文件!具体原理请看下方的Filter实现原理图示!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class AdminFilter implements Filter { public AdminFilter () { System.out.println("1. 构造器方法" ); } @Override public void init (FilterConfig filterConfig) throws ServletException { System.out.println("2. 初始化方法" ); } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("3. doFilter方法" ); HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpSession session = httpServletRequest.getSession(); Object user = session.getAttribute("user" ); if (user == null ){ servletRequest.getRequestDispatcher("/login.jsp" ).forward(servletRequest, servletResponse); }else { filterChain.doFilter(servletRequest, servletResponse); } } @Override public void destroy () { System.out.println("5. 销毁方法" ); } }
web.xml:
url-pattern
:配置拦截路径,拦截到的请求使用Filter实现类中的doFilter()方法捕捉处理!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <filter > <filter-name > AdminFilter</filter-name > <filter-class > com.atguigu.filter.AdminFilter</filter-class > </filter > <filter-mapping > <filter-name > AdminFilter</filter-name > <url-pattern > /admin/*</url-pattern > </filter-mapping >
Filter实现原理:
3、Filter生命周期
构造器方法
init 初始化方法(前两步web工程启动时执行,Filter已经创建)
doFilter 过滤方法,每次拦截到请求,就会执行
destroy 销毁,停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤器)
4、FilterConfig类
Filter 过滤器的配置文件类。
Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。
FilterConfig 类的作用是获取 filter 过滤器的配置内容:
获取 Filter 的名称 filter-name 的内容
获取在 Filter 中配置的 init-param 初始化参数
获取 ServletContext 对象
web.xml配置初始化参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <filter > <filter-name > AdminFilter</filter-name > <filter-class > com.atguigu.filter.AdminFilter</filter-class > <init-param > <param-name > username</param-name > <param-value > itnxd</param-value > </init-param > </filter > <filter-mapping > <filter-name > AdminFilter</filter-name > <url-pattern > /admin/*</url-pattern > </filter-mapping >
获取配置信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override public void init (FilterConfig filterConfig) throws ServletException { System.out.println("2. 初始化方法" ); String filterName = filterConfig.getFilterName(); System.out.println("filter-name的值是:" + filterName); String username = filterConfig.getInitParameter("username" ); System.out.println("初始化参数的username的值是:" + username); System.out.println(filterConfig.getServletContext()); }
5、FilterChain过滤器链
多个过滤器时候的执行顺序!
多个过滤器时执行过程图示:
Filter1代码:
1 2 3 4 5 6 7 8 9 10 @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Filter1的前置代码1" ); System.out.println("Filter1的线程:" + Thread.currentThread().getName()); filterChain.doFilter(servletRequest, servletResponse); System.out.println("Filter1的线程:" + Thread.currentThread().getName()); System.out.println("Filter1的后置代码2" ); }
Filter2代码:
1 2 3 4 5 6 7 8 9 10 @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Filter2的前置代码1" ); System.out.println("Filter2的线程:" + Thread.currentThread().getName()); filterChain.doFilter(servletRequest, servletResponse); System.out.println("Filter2的线程:" + Thread.currentThread().getName()); System.out.println("Filter2的后置代码2" ); }
总结: 这些总结可以根据上方图示来理解!
Filter执行顺序由web.xml顺序决定
若删除Filter2的filterChain.doFilter方法,则访问不到资源,直接跳回Filter1
若删除Filter1的filterChain.doFilter方法,则Filter2也无法执行
同一个拦截目标经过的Filter共用一个线程
同一个拦截目标经过的Filter中的request域共用(一次请求,一次响应 )
6、拦截路径
精确匹配 1.html
目录匹配 admin/*
后缀名匹配 *.jsp
注意:
url-pattern
标签可以有多个!
Filter过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!
1 2 3 4 5 6 7 8 9 10 11 <filter > <filter-name > Filter2</filter-name > <filter-class > com.atguigu.filter.Filter2</filter-class > </filter > <filter-mapping > <filter-name > Filter2</filter-name > <url-pattern > /target.jsp</url-pattern > <url-pattern > /admin/*</url-pattern > <url-pattern > /*.html</url-pattern > </filter-mapping >
二、ThreadLocal
1、概述
ThreadLocal 的作用,它可以解决多线程的数据安全问题 。
ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)
可以用来处理项目中的数据库事务问题,即始终使用ThreadLocal维护一个Connection连接,可以进行事务管理!
补充 :对于数据库事务问题,据我理解,只要所有数据库操作都传入一个连接对象,且不在操作中进行关闭连接就可以达到事务操作的目的!正如康师傅的JDBC中的考虑事务操作的通用增删改查的实现,点击这里!
2、ThreadLocal 特点
ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程 )
每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal 对象实例。
每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型
ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。
3、ThreadLocal使用
ThreadLocal中定义了set、get、remove方法,用来关联 / 取出 / 移除数据
ThreadLocalTest类:
该类中调用多个线程分别使用ThreadLocal绑定一个随机数,会发现每个线程始终绑定一个随机数,即使在该线程结束前调用别的类,在别的类中打印线程名和绑定的值仍然是一样的!
作用主要就是为了解决多线程问题,只要该ThreadLocal没有结束前,使用是一个线程,绑定的内容也是唯一的!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public class ThreadLocalTest { public static ThreadLocal<Object> threadLocal = new ThreadLocal<>(); private static Random random = new Random(); public static class Task implements Runnable { @Override public void run () { Integer i = random.nextInt(1000 ); String name = Thread.currentThread().getName(); System.out.println("线程【" + name +"】生成的随机数是:" + i); threadLocal.set(i); try { Thread.sleep(3000 ); } catch (InterruptedException e) { e.printStackTrace(); } new OrderService().createOrder(); Object value = threadLocal.get(); System.out.println("在线程【" + name +"】快结束时得到的随机数是:" + i); } } public static void main (String[] args) { Task task = new Task(); for (int i = 0 ; i < 3 ; i ++){ new Thread(task).start(); } } }
OrderService类:
1 2 3 4 5 6 7 8 9 public class OrderService { public void createOrder () { String name = Thread.currentThread().getName(); System.out.println("当前线程【" + name +"】保存的数据是:" + ThreadLocalTest.threadLocal.get()); } }
三、Tomcat配置错误页面
error-code:错误码(404,500)
location:错误时要跳转的路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <error-page > <error-code > 500</error-code > <location > /pages/error/error500.jsp</location > </error-page > <error-page > <error-code > 404</error-code > <location > /pages/error/error404.jsp</location > </error-page >