1.什么是Filter
- 它是 JavaWeb 的三大组件之一(三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器)
- 它是 JavaEE 的规范(也就是接口)
- 它的作用是:拦截请求,过滤响应
tips:
- 拦截请求常见的应用场景有:
- 权限检查
- 日记操作
- 事务管理
2.Filter初体验
在web工程下,有一个 admin 目录。这个 admin 目录下的所有资源(html 页面、jpg 图片、jsp 文件、等等)都必须是用户登录之后才允许访问:
- 方式1:用户登录之后都会把用户登录的信息保存到Session 域中。所以要检查用户是否登录,可以判断Session中否包含有用户登录的信息即可:
// 在admin目录下的a.jsp <body> <% Object user = session.getAttribute("user"); // 如果等于 null,说明还没有登录 if (user == null) { request.getRequestDispatcher("/login.jsp").forward(request,response); return; } %> I am a.jsp </body> // web目录下的login.jsp <body> login.jsp页面 </body>
未登录的状态下输入http://localhost:8080/book/admin/a.jsp则会转发到login.jsp中。但是对于
a.html
这种方式实现不了,因为html页面不能写Java代码
- 方式2:使用Filter:
public class AdminFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } /** * doFilter 方法,专门用于拦截请求。可以做权限检查 */ @Override public 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("/login.jsp").forward(servletRequest,servletResponse); return; }else { // 让程序继续往下访问用户的目标资源 filterChain.doFilter(servletRequest, servletResponse); } } @Override public void destroy() { } }
// web.xml <!--filter 标签用于配置一个 Filter 过滤器--> <filter> <!--给 filter 起一个别名--> <filter-name>AdminFilter</filter-name> <!--配置 filter 的全类名--> <filter-class>com.psj.filter.AdminFilter</filter-class> </filter> <!--filter-mapping 配置 Filter 过滤器的拦截路径--> <filter-mapping> <!--filter-name 表示当前的拦截路径给哪个 filter 使用--> <filter-name>AdminFilter</filter-name> <!--url-pattern 配置拦截路径 / 表示请求地址为:http://ip:port/工程路径/ ,映射到 IDEA 的 web 目录 /admin/* 表示请求地址为:http://ip:port/工程路径/admin/* 当浏览器输入如http://localhost:8080/book/admin/a.jsp就会进行拦截操作 --> <url-pattern>/admin/*</url-pattern> </filter-mapping>
3.Filter的生命周期
- Filter 的生命周期包含几个方法:
- 构造器方法
- init 初始化方法:第 1,2 步在 web 工程启动的时候执行(Filter 已经创建)
- doFilter 过滤方法:每次拦截到请求,就会执行
- destroy 销毁:停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤器)
4.FilterConfig类
- Filter过滤器的配置文件类
- Tomcat 每次创建 Filter 的时候,也会同时创建一个
FilterConfig
类,这里包含了 Filter 配置文件的配置信息- FilterConfig 类的作用是获取 filter 过滤器的配置内容:
- 获取 Filter 的名称
filter-name
的内容,使用FilterConfig
中的getFilterName()
- 获取在 Filter 中配置的
init-param
初始化参数,使用FilterConfig
的getInitParameter("xxx")
- 获取
ServletContext
对象,使用FilterConfig
的getServletContext()
5.FilterChain过滤器链
- FilterChain即过滤器链(多个过滤器如何一起工作)
tips:
- 在下图中,如果Filter1没有执行
chain.doFilter
方法是不会执行Filter2的doFiter
中的所有代码的
6.Filter的拦截路径
- Filter的拦截路径有三种匹配方法:
- 精确匹配:
<url-pattern>/target.jsp</url-pattern>
,表示请求地址必须为http://ip:port/工程路径/target.jsp
- 目录匹配:
<url-pattern>/admin/*</url-pattern>
,表示请求地址必须为http://ip:port/工程路径/admin/*
- 后缀名匹配:
<url-pattern>*.html</url-pattern>
,表示请求地址必须以.html
结尾才会拦截到tips:
- Filter它只关心请求的地址是否匹配,不关心请求的资源是否存在,所以采用后缀名匹配时可以写任意后缀名如
.abc
7.ThreadLocal
7.1 概念和特点
- 可以解决多线程的数据安全问题
- 可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)
- 可以像 Map 一样存取数据,key 为当前线程
- 每一个
ThreadLocal
对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal
对象实例- 每个
ThreadLocal
对象实例定义的时候,一般都是static
类型ThreadLocal
中保存数据,在线程销毁后。会由 JVM 虚拟自动释放
7.2 入门
// 不使用ThreaddLocal public class ThreadLocalTest { public final static Map<String, Object> data = new ConcurrentHashMap<>(); private static Random random = new Random(); public static class Task implements Runnable { @Override public void run() { // 随机生成一个变量(即线程要关联的数据),然后以当前线程为key保存到Map中 int i = random.nextInt(1000); String name = Thread.currentThread().getName(); System.out.println("线程[" + name + "]生成的随机数是:" + i); data.put(name, i); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 该线程执行其他类的方法,但是和线程绑定的数据也是能在执行该方法时获取到 new OrderService().createOrder(); Object o = data.get(name); System.out.println("在线程["+name+"]快结束时取出关联的数据是:" + o); } } public static void main(String[] args) { for (int i = 0; i < 3; i++) { new Thread(new Task()).start(); } } } // OrderService类 public class OrderService { public void createOrder() { String name = Thread.currentThread().getName(); System.out.println("OrderService 当前线程[" + name + "]中保存的数据是:" + ThreadLocalTest.data.get(name)); } } // 打印内容如下: 线程[Thread-1]生成的随机数是:291 线程[Thread-2]生成的随机数是:198 线程[Thread-0]生成的随机数是:837 OrderService 当前线程[Thread-2]中保存的数据是:198 OrderService 当前线程[Thread-0]中保存的数据是:837 OrderService 当前线程[Thread-1]中保存的数据是:291 在线程[Thread-1]快结束时取出关联的数据是:291 在线程[Thread-2]快结束时取出关联的数据是:198 在线程[Thread-0]快结束时取出关联的数据是:837
// 使用ThreadLocal,简化了代码 public class ThreadLocalTest { // public final static Map<String, Object> data = new ConcurrentHashMap<>(); public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>(); private static Random random = new Random(); public static class Task implements Runnable { @Override public void run() { // 随机生成一个变量(即线程要关联的数据),然后以当前线程为key保存到Map中 int i = random.nextInt(1000); String name = Thread.currentThread().getName(); System.out.println("线程[" + name + "]生成的随机数是:" + i); // data.put(name, i); threadLocal.set(i); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } new OrderService().createOrder(); // Object o = data.get(name); Object o = threadLocal.get(); System.out.println("在线程[" + name + "]快结束时取出关联的数据是:" + o); } } } public class OrderService { public void createOrder() { String name = Thread.currentThread().getName(); System.out.println("OrderService 当前线程[" + name + "]中保存的数据是:" + ThreadLocalTest.threadLocal.get()); } }
7.3 实际使用
tips:
- 如上图中可以看到,假设在Dao层中出现异常,如果直接捕获了异常,则在Service层等就不会有异常,就无法执行到
catch
代码块中的rollback
,所以在Dao中可以捕获异常,但是也要向外抛出一个异常,或者直接向外抛出异常
8.Filter和ThreadLocal的结合
- 执行
filterChain.doFilter()
方法因为会调用目标资源,当调用的目标资源是html
是就返回页面给客户端,当调用的目标资源时Servlet程序时,就相当于执行Servlet中的代码内容
9.配置错误页面
// web.xml <!--error-page 标签配置,服务器出错之后,自动跳转的页面--> <error-page> <!--error-code 是错误类型--> <error-code>500</error-code> <!--location 标签表示。要跳转去的页面路径--> <location>/pages/error/error500.jsp</location> </error-page> <!--error-page 标签配置,服务器出错之后,自动跳转的页面--> <error-page> <!--error-code 是错误类型--> <error-code>404</error-code> <!--location 标签表示。要跳转去的页面路径--> <location>/pages/error/error404.jsp</location> </error-page>