Web基础-Filter过滤器

1.什么是Filter

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

tips:

  • 拦截请求常见的应用场景有:
    • 权限检查
    • 日记操作
    • 事务管理

2.Filter初体验

  • 在web工程下,有一个 admin 目录。这个 admin 目录下的所有资源(html 页面、jpg 图片、jsp 文件、等等)都必须是用户登录之后才允许访问:

    • 方式1:用户登录之后都会把用户登录的信息保存到Session 域中。所以要检查用户是否登录,可以判断Session中否包含有用户登录的信息即可:
    // 在admin目录下的a.jsp
    
        <%
            Object user = session.getAttribute("user");
            // 如果等于 null,说明还没有登录
            if (user == null) {
                request.getRequestDispatcher("/login.jsp").forward(request,response);
                return;
            }
        %>
        I am a.jsp
    
    // web目录下的login.jsp
    
        login.jsp页面
    
    

    ​ 未登录的状态下输入http://localhost:8080/book/admin/a.jsp则会转发到login.jsp中。但是对于a.html这种方式实现不了,因为html页面不能写Java代码

    • 方式2:使用Filter:

    Web基础-Filter过滤器_第1张图片

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-name>AdminFilterfilter-name>
    
    <filter-class>com.psj.filter.AdminFilterfilter-class>
filter>

<filter-mapping>
    
    <filter-name>AdminFilterfilter-name>
    
    <url-pattern>/admin/*url-pattern>
filter-mapping>

3.Filter的生命周期

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

4.FilterConfig类

  • Filter过滤器的配置文件类
  • Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息
  • FilterConfig 类的作用是获取 filter 过滤器的配置内容
    • 获取 Filter 的名称 filter-name 的内容,使用FilterConfig中的getFilterName()
    • 获取在 Filter 中配置的 init-param 初始化参数,使用FilterConfiggetInitParameter("xxx")
    • 获取 ServletContext 对象,使用FilterConfiggetServletContext()

5.FilterChain过滤器链

  • FilterChain即过滤器链(多个过滤器如何一起工作)

tips:

  • 在下图中,如果Filter1没有执行chain.doFilter方法是不会执行Filter2的doFiter中的所有代码的

Web基础-Filter过滤器_第2张图片


6.Filter的拦截路径

  • Filter的拦截路径有三种匹配方法:
    • 精确匹配:
      • /target.jsp,表示请求地址必须为http://ip:port/工程路径/target.jsp
    • 目录匹配:
      • /admin/*,表示请求地址必须为http://ip:port/工程路径/admin/*
    • 后缀名匹配:
      • *.html,表示请求地址必须以.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 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 实际使用

Web基础-Filter过滤器_第3张图片

tips:

  • 如上图中可以看到,假设在Dao层中出现异常,如果直接捕获了异常,则在Service层等就不会有异常,就无法执行到catch代码块中的rollback,所以在Dao中可以捕获异常,但是也要向外抛出一个异常,或者直接向外抛出异常

8.Filter和ThreadLocal的结合

Web基础-Filter过滤器_第4张图片

  • 执行filterChain.doFilter()方法因为会调用目标资源,当调用的目标资源是html是就返回页面给客户端,当调用的目标资源时Servlet程序时,就相当于执行Servlet中的代码内容

9.配置错误页面

// web.xml

<error-page>
    
    <error-code>500error-code>
    
    <location>/pages/error/error500.jsplocation>
error-page>

<error-page>
    
    <error-code>404error-code>
    
    <location>/pages/error/error404.jsplocation>
error-page>

你可能感兴趣的:(Web,web,java-ee,filter)