Filter 过滤器

本文详细介绍了Java Web中的Filter过滤器,包括其作用、生命周期、配置及使用步骤。Filter主要用于拦截请求和响应,常用于权限检查、日志记录等场景。通过实例展示了如何实现登录拦截,确保只有登录用户才能访问特定资源。此外,还讨论了Filter的配置、FilterChain的使用以及Filter的路径匹配规则。最后,简单介绍了ThreadLocal在多线程环境下的线程局部变量管理。
摘要由CSDN通过智能技术生成

目录

        一、什么是 Filter 过滤器?

        二、应用举例 

        三、使用过滤器的步骤

        四、Filter 的生命周期

        五、FilterConfig 类

        六、FilterChain 类

        七、Filter 的拦截路径

         八、 ThreadLocal 的介绍


一、什么是 Filter 过滤器?

 当浏览器向服务器发送请求想要浏览某个资源时,首先会经过 Filter 过滤器,判断是否符合权限

1Filter 过滤器它是 JavaWeb 的三大组件之一。三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器 

2 Filter 过滤器它是 JavaEE 的规范。也就是接口
3 Filter 过滤器它的作用是: 拦截请求 ,过滤响应。
拦截请求常见的应用场景有:
1 、权限检查
2 、日记操作
3 、事务管理
……等等

二、应用举例 

假设我们在 web 目录下有一个 admin 目录,里面有一些资源【图片,html,页面....】 ,我们希望登录过后才能访问,没有登录就跳到登录界面。

那么按照我们之前学习的内容:应该是在登录成功的时候,将 用户 保存到 session 域中,在跳转资源的时候,判断session域中用户是否为空,为空说明还没有登录,则跳转到登录界面。不为空可以访问资源。

    <%
        /*判断,如果user为空,则跳转到登录页面。*/
        Object user = session.getAttribute("user");
        if (user == null){
            request.getRequestDispatcher("/login.jsp").forward(request,response);
            return;
        }
    %>

 当我们访问 jsp 页面时,没有登录他会跳转到登录页面。

 当我们没有登录时,仍然能访问 a.html 页面还有图片,所以说这种方法是有局限性的。

 

它仅仅只能在 jsp 页面上判断,html,图片就无法判断 session域是否为空了。所以我们需要使用  Filter 过滤器。

 三、使用过滤器的步骤

1、 写一个类实现 javax.servlet.Filter 接口,并且实现 Filter接口中的三个方法。init,doFilter,destroy 。

2、在 doFilter方法中,编写拦截操作。

@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //
        HttpServletRequest request  = (HttpServletRequest) servletRequest;
        Object user = request.getSession().getAttribute("user");
        if (user == null){
            //拦截操作:如果user为空,说明没有登录。
            //跳转到登录界面servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
            return;
        }else{
            //可以访问资源
            //具体可以访问哪些资源,在 web.xml 文件中配置
            // 这段代码很重要,如果不写就算登陆成功也访问不了任何资源。
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }

3、在 web.xml 文件中配置可以访问哪些资源。与配置Servlet类似。

拦截的路径可以写多个地址。 

    <filter>
        <!--给 filter 起一个别名-->
        <filter-name>FilterTest</filter-name>
        <!--完整类名-->
        <filter-class>yangzhaoguang.FilterTest</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FilterTest</filter-name>
        <!--
            配置拦截路径:
                / : 表示:http;//ip:port/工程路径/
                /admin/* : 表示对admin目录下的所有资源都进行拦截操作。
        -->
        <url-pattern>/admin/*</url-pattern>
        <url-pattern>/admin/b.jsp</url-pattern>
    </filter-mapping>

这个时候我们在去访问admin下的资源:都可以拦截到了。如果还能访问到资源,说明浏览器还有缓存,清除一下就好了。

 以上是未登录成功拦截到的情况,下面演示以下登录成功后,能不能访问到。

提供一个登录页面和 LoginServlet :

<form action="/filter/loginServlet" method="post">
用户名:<input type="text" name="username" ><br>
密码:<input type="password" name="password" ><br>
    <input type="submit" value="登录">
</form>
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");


        if ("admin".equals(username) && "admin".equals(password)){
            //登录成功,将用户信息保存到 session 域中
            request.getSession().setAttribute("user",username);
            request.getRequestDispatcher("/login_success.jsp").forward(request,response);
        }else{
            //没有登录成功,调回登录界面
            request.getRequestDispatcher("/login.jsp").forward(request,response);

        }
    }
}

 登录成功后,admin下的所有资源都能访问到,就没有问题了。

, 

 四、Filter 的生命周期

类似于Servlet 中的 init ,service ,destroy 方法。

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

 

五、FilterConfig 类

FilterConfig 类见名知义,它是 Filter 过滤器的配置文件类。
Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。
        
        
        <!--在 filter 标签里,配置FilterConfig参数。-->
        <init-param>
            <param-name>name1</param-name>
            <param-value>value1</param-value>
        </init-param>
        <!--也可以配置多个-->
        <init-param>
            <param-name>name2</param-name>
            <param-value>value2</param-value>
        </init-param>
FilterConfig 类的作用是获取 filter 过滤器的配置内容
        1、获取 Filter 的名称 filter-name 的内容,就是在 web.xml 文件中起的别名。
filterConfig.getFilterName()
        2、获取在 Filter 中配置的 init-param 初始化参数
filterConfig.getInitParameter("name")
        3、获取 ServletContext 对象
filterConfig.getServletContext()

 初始化时获取参数:

 @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("2 init初始化方法执行");
//         1、获取 Filter 的名称 filter-name 的内容
        System.out.println("获取 filter-name:"+ filterConfig.getFilterName());
//         2、获取在 Filter 中配置的 init-param 初始化参数
        System.out.println("init-param中的 name1 的value" + filterConfig.getInitParameter("name1"));
        System.out.println("init-param中的 name2 的value" + filterConfig.getInitParameter("name2"));
//         3、获取 ServletContext 对象
        System.out.println("ServletContext对象:"+filterConfig.getServletContext());

    }

 六、FilterChain 类

FilterChain 就是过滤器链(多个过滤器如何一起工作)

我们可以看到执行顺序和图中的顺序一样

1、多个过滤器先后执行顺序是由 web.xml 文件决定的,先配置哪个过滤器,哪个过滤器就会先执行。

如果先配置 Filter2 。

 先配置 Filter2,Filter2就会先执行。

2、 Filter中 靠 filterChain.doFilter(); 来继续访问下一步的资源,如果没有这段代码,后面的资源将不会被访问,代码也不会被执行。

将Filter1中的 filterChain.doFilter() 去掉,那么后面的 Filter2 和 b.jsp 页面都不会执行到。

3、从图中也可以看出来,这一整个流程都是在一个请求中完成的,也就是他们共享同一个请求域。 

七、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 结尾才会拦截到
< url-pattern >*.do</ url-pattern >
以上配置的路径,表示请求地址必须以 .do 结尾才会拦截到
< url-pattern >*.action</ url-pattern >
以上配置的路径,表示请求地址必须以 .action 结尾才会拦截到
Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!

八、 ThreadLocal 的介绍

ThreadLocal 是 JDK 中的一个类,ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量【(可以是普通变量,可以是对象,也可以是数组,集合】与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。

ThreadLocal 的特点:
        1、 ThreadLocal 可以为当前线程关联一个数据。如果关联多个数据,后面的数据会覆盖前面的数据,像map集合中的key重复,value会被覆盖(它可以像 Map 一样存取数据,key 为当前线程
        2、 每一个 ThreadLocal 对象,只能为当前线程关联一个数据 ,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例。
        3、每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型
        4、 ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。

与synchronized的关系:

有些文章拿ThreadLocal和synchronized比较,其实它们的实现思想不一样。

  • synchronized是同一时间最多只有一个线程执行,所以变量只需要存一份,算是一种时间换空间的思想
  • ThreadLocal是多个线程互不影响,所以每个线程存一份变量,算是一种空间换时间的
构造方法摘要
ThreadLocal()
          创建一个线程本地变量。
 Tget()
          取出当前线程池中的变量值
 voidremove()
          移除此线程局部变量当前线程的值。
 voidset(T value)
          存入 线程池 的变量值为value

 使用 ThreadLocal 是不需要设置 key 的值,key 就是当前线程的名字。

public class ThreadLocalTest {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            //创建多个线程
            new Thread(new Task()).start();
        }
    }

    /* <> 指的是 value的类型*/
    public  static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
    private static Random random = new Random();

    //创建一个线程任务
    public static class Task implements Runnable {
        @Override
        public void run() {
            //获取当前线程的名字
            String name = Thread.currentThread().getName();
            //获取一个随机数
            int i = random.nextInt(200);
            // 将当前线程与 一个值 相关联。
            threadLocal.set(i);
            System.out.println("存入线程[ " + name + " ] 关联的值:" + threadLocal.get());

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //取出相关联的值
            System.out.println("取出线程[ " + name + " ] 关联的值:" + threadLocal.get());
        }
    }
}

我们可以看到 ThreadLocal 不仅是安全的,并且它并不影响多个线程的使用。并且每个线程都会保存一个变量。 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲨瓜2号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值