Filter&Listener学习

Filter:过滤器

web中的过滤器:当访问服务器资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。
过滤器的作用:
一般用于完成通用的操作。如:登录验证、统一编码处理、(在servlet上需要进行统一编码,在过滤器上使用,会简化编码)、敏感字符过滤(比如打游戏的时候,你骂人了,则可以把骂人的话改成xxx)
快速入门
1.步骤:
1.定义一个类,实现接口Filter
2.复写方法
3.配置拦截路径
4.放行
注意:导入的时候,一定要实现servlet下面的这个。
在这里插入图片描述
重写里面的方法,在doFilter写上一个输出的方法,然后对该类进行配置拦截路径;拦截路径有两个方法进行配置,1.web.xml 2.注解
如下图是,进行web.xml进行配置
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020061414261491.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjAwODE2OA==,size_16,color_FFFFFF,t_70
访问当前项目控制台输出结果如图所示:
在这里插入图片描述
网页输出结果如图所示:由于该拦截器没有放行,所以网页没有输出
在这里插入图片描述
这时候,需要进行放行,代码如下:
在这里插入图片描述
此时再进行页面访问,结果如下;可以进行放行
在这里插入图片描述
过滤器的细节
1.web.xml配置
2.过滤器执行流程
3.过滤器生命周期方法
4.过滤器配置详情
5.过滤器链(配置多个过滤器)
1.web.xml配置
先把注解配置给注释掉,再进行配置。在这里插入图片描述
经过访问发现可以成功
2.过滤器的执行流程
新建一个FilterDemo2类;在这里插入图片描述
然后在index.jsp中写上java代码,如下:
在这里插入图片描述
进行访问,结果如下:在这里插入图片描述
所以,它的执行是先执行放行之前的消息,然后执行访问资源的内容,最后,再回来执行放行之后的内容;根据执行内容它们的功能如下:
在这里插入图片描述
在这里插入图片描述
3.过滤器的生命周期

代码如下:
package cn.sainan114.web;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * FileName: ${NAME}
 *
 * @Author:luguobao Date: ${DATA}15:03
 * Description:
 * History:
 * <author>   <time>   <version>   <desc>
 * 作者姓名    修改时间    版本号       描述
 */
@WebFilter("/*")
public class FilterDemo3 implements Filter {
    /**
     * 在服务器关闭后,Filter对象被销毁,如果服务器正常关闭,会执行destory方法
     */
    public void destroy() {
        System.out.println("dostory...");
    }

    /**
     * 每一次请求被拦截资源时,会执行
     * @param req
     * @param resp
     * @param chain
     * @throws ServletException
     * @throws IOException
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("doFilter...");
        chain.doFilter(req, resp);
    }
    /**
     * 在服务器创建Filter对象,调用init方法
     * @param config
     * @throws ServletException
     */
    public void init(FilterConfig config) throws ServletException {
        System.out.println("init...");
    }
}

所以 destory 方法 init方法 它们是只执行一次,doFilter方法只要请求就会执行,所以应该是执行多次。
代码如下:
在这里插入图片描述
每次访问一次页面就会显示doFilter 和 index.jsp这是由于在jsp页面中输出的。
当关闭服务器时,会执行destory一次
在这里插入图片描述
一般来说init方法用于加载资源 destory方法用于释放资源。
4.过滤器配置详解:
拦截路径配置:
1.具体资源路径:/index.jsp 只有访问index.jsp资源时,过滤器才会被执行,用的少,应该过滤器一般用于通用操作;
2.目录拦截:/user/*访问/user下所有资源时,过滤器都会被执行
3.后缀名拦截: .jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
4.拦截所有资源:/
访问所有资源时,过滤器都会被执行
例:目录拦截测试:
先写两个类如下:

package cn.sainan114.web.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/user/findAllServlet")
public class FindAllServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("findAllServlet");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }
}

package cn.sainan114.web.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/user/updateServlet")
public class UpdateServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("updateservlet");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }
}

再写一个监听器如下:

package cn.sainan114.web;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/user/*")
public class FilterDemo4 implements Filter {
    /**
     * 在服务器关闭后,Filter对象被销毁,如果服务器正常关闭,会执行destory方法
     */
    public void destroy() {
        System.out.println("dostory...");
    }

    /**
     * 每一次请求被拦截资源时,会执行
     * @param req
     * @param resp
     * @param chain
     * @throws ServletException
     * @throws IOException
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("doFilter...");
        chain.doFilter(req, resp);


    }

    /**
     * 在服务器创建Filter对象,调用init方法
     * @param config
     * @throws ServletException
     */
    public void init(FilterConfig config) throws ServletException {
        System.out.println("init...");
    }

}

此时,当访问user路径下的资源时,会启动拦截器,会输出doFilter…然后再进行放行再输出updateservlet 或 findAllServlet。依次访问前两个servlet会得到如下结果:
在这里插入图片描述
但是,当我访问index.jsp时,就可以发现不会执行doFilter了。
在这里插入图片描述
因为拦截器没有对它进行拦截,它就直接指向资源了。
注意:如果拦截器拦截了,没有进行放行,那么就不会执行两个servlet了。
当访问资源的时候,在这里插入图片描述
由于没有放行,无法执行servlet里面的内容,所以结果是这样的;
在这里插入图片描述
4.过滤器拦截方式配置:资源被访问的方式
1.注解配置
在WebFilter注解下,需要设置一个属性:在这里插入图片描述
设置了dispatcherTypes属性以后,可以设置资源被访问到的方式
1.REQUEST:默认值,浏览器直接请求资源
2.FORWARD:转发访问资源
3.INCLUDE:包含访问资源
4.ERROR:错误跳转资源
5.ASYNC:异步访问资源
测试:
当浏览器直接请求index.jsp的时候,拦截器启动,当转发请求的时候,拦截器不执行。
代码如下:
在这里插入图片描述
在这里插入图片描述
下面这个代表的是直接请求时,进行拦截;
测试如下;先清空控制台,然后再进行访问http://localhost/day19_fileter_listener_war_exploded/user/updateServlet,结果如下:
在这里插入图片描述
所以,很显然没有输出doFilter…所以,对于转发的请求它不拦截的。 如果我直接访问index.jsp在这里插入图片描述
这个时候,发现过滤器是可以被执行的
现在,我进行更改在这里插入图片描述
这个代表的是只有转发的时候,进行执行。直接请求是不可以的。
这是直接访问的结果
在这里插入图片描述
当我访问http://localhost/day19_fileter_listener_war_exploded/user/updateServlet时;结果如下:
在这里插入图片描述
所以说明只有转发时,才会执行该过滤器;
注意:dispatcherTypes对应的是一个数组。所以,我们在使用的过程中可以使用数组来进行配置
在这里插入图片描述
这样代表着既可以通过转发 也可以通过直接请求的时候,都执行过滤器。

2.web.xml配置

用的是这个标签
在这里插入图片描述
上图配置和在这里插入图片描述
它们产生的效果是一样的。
多个过滤器
执行顺序:如果有两个过滤器:过滤器1和过滤器2(假设这两个过滤器,过滤路径一样)
1. 过滤器1
2. 过滤器2
3. 资源执行
4. 过滤器2
5. 过滤器1
输出结果如下;
在这里插入图片描述
过滤器先后顺序问题:
1.注解配置的过滤器:按照类名的字符串比较规则,值小的先执行;
如上:6<7所以上面先执行6
但是如果一个是FilterDemo17 和 FilterDemo6则先执行FilterDemo17 因为 1和7比 1<6(一个一个字符相比较的)
2.web.xml配置:谁定义在上面,谁先执行;
案例:登录页面
原理分析:
在这里插入图片描述
当所有的路径进行请求资源的时候,会先给过滤器进行判断,如果是登录相关的,则进行放行,如果不是则转发到登入相关的页面,并给出提示。那么如何判断它是不是登录过呢,当登入成功的时候,它会给一个session将相关的信息存起来,如果你登入过,只要浏览器没有关闭,你第二次登录的时候,它会在过滤器中进行判断这个过滤器,如果判断出来有这个session则会进行放行。
注意在idea中导入其他人的项目可以参考一下这个博客https://blog.csdn.net/u010632547/article/details/104892519
案例2:过滤敏感词汇
首先:过滤器是会收到req,和resp 过滤器中有方法将req(请求中指定参数的数据)取出来,这时候,可以判断一下是不是有敏感词汇,有的话就可以将敏感词进行代替,如果req中可以进行设置参数的话,再将替换过的数据进行设置,再给它发送到指定的资源即可。
但是由于req中没有对参数进行设置的方法,故实际的情况是这样的,对request对象的getParameter方法进行增强产生一个新的request对象,然后放行,将新的request对象传入。
增强对象的功能:用到设计模式
1.代理模式
概念:
1.真实对象:被代理的对象
2.代理对象:
3.代理模式:代理对象代理真实对象,达到增强真实对象功能的目的。
分析:分析一下本例子中为什么需要使用到动态代理;
动态代理的功能如下:它可以对方法进行增强,比如说增强参数列表,增强返回值类型,增强方法体的执行逻辑。然后在本事例中,用getParameter方法,可以得到指定参数所对应的值,但是由于没有设置参数的值,所以,无法实现对参数进行设置,那么如果我们使用动态代理对getParameter进行改造的话,那么就可以在这个方法中对敏感词汇进行操作,如果有敏感词汇的话,进行过滤,这样以后在调用该方法的时候,就直接可以获取到过滤完敏感词汇后的参数所对应的值了。
实现方式:
1.动态代理:在内存中形成代理类;用的多
2.静态代理:有一个类文件描述的代理模式;用的少,故这里用动态代理进行实现。
思路分析
实现步骤:
1. 代理对象和真实对象实现相同的接口
2. 代理对象 = Proxy.newProxyInstance();
3. 使用代理对象调用方法。
4. 增强方法
因为它们有相同的方法,所以代理对象可以和真实对象一样调用相同的方法,同时因为方法相同,所以可以进行方法的重写,来给增强方法(增加功能)
代码测试:
写一个接口和一个实现类
在这里插入图片描述
在这里插入图片描述
再写一个测试类,先测试一下invoke 和 method的名称

public class ProxyTest {
    public static void main(String[] args) {
        //1.创建真实对象
        Lenovo lenovo = new Lenovo();
        /**
         * 这个时候我发现我不想执行这个sale了,我想改变一点自己的花样
         * 这时候就可以用动态代理进行增强lenovo对象
         * 第一个参数为对象类加载器,因为这个代理对象也需要进行执行,代理对象和真实对象用一样的类加载器(固定写法)
         * 第二个参数为真实对象实现的接口(固定写法)
         * 第三个对象为InvocationHandler,用匿名类来进行创建,(基本上也是固定写法)
         * 返回的对象proxy,代表的就是代理对象。因为这里知道它们实现的是SaleComputer接口,故可以强转为该接口
         * 所以proxy 和 lenovo 都实现了SaleComputer 所以将来调用方法的时候,就可以不用lenovo进行调用了
         * invoke代表的是代理逻辑编写的方法:代理对象调用的所有的方法都会触发该方法执行
         */
        SaleComputer proxy = (SaleComputer)Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            /**
             * 参数 1:1.proxy:代理对象
             * 参数 2:Method对象 每当通过代理对象调用方法的时候,这个方法会被封装成为一个对象,传到这里面来。方法封装对象会成为Method(反射中有讲)
             *
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("该方法执行了...");
                String name = method.getName();
                return null;
            }
        });

        //2.调用方法
        String computer = proxy.sale(8000);
        System.out.println(computer);
    }
}

结果如下:发现结果正确,结果写在代码中
在这里插入图片描述
测试args
在这里插入图片描述
注意:该代理对象执行sale是失败的,就像卖电脑一样,真正执行的还是lenovo
为了解决这个问题:写代码如下:
在这里插入图片描述
代码分析:当proxy去执行sale方法的时候,会去执行invoke方法,这个时候,会执行lenovo的sale方法,参数为8000,这时候,理论上会在控制台输出花了8000块钱买了一台电脑,obj应该等于联想电脑;但是,由于这个return null所以输出computer结果应该为null。
结果如下:
在这里插入图片描述
符合预期
但把invoke的返回值改为obj以后,理论上输出computer的时候,应该结果是联想电脑,经过测试:结果如下:符合预期
在这里插入图片描述
此时,我把方法改成show();
在这里插入图片描述
show没有返回值,所以,也没有接收,上面那么写应该也没事。
如果它的返回值是展示电脑,说明我们用动态代理这种方法可以实现将具体类的方法来进行执行。
测试如下:
在这里插入图片描述
增强方法
现在可以用代理对象来执行真实对象的方法了,这时候可以对真实对象进行增强了,增强方式有三种
1.增强参数列表
2.增强返回值类型
3.增强方法体执行逻辑
测试:增强参数列表
现在参数都可以拿到了,那么增强参数列表就比较简单了;
在这里插入图片描述
结论如下:
在这里插入图片描述
测试:增强返回值
在这里插入图片描述
结果如下:在这里插入图片描述
测试:增强方法体的逻辑
在这里插入图片描述
结果如下:
在这里插入图片描述
总代码如下:

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * FileName: ProxyTest
 *
 * @Author:luguobao Date: 14:40
 * Description:
 * History:
 * <author>   <time>   <version>   <desc>
 * 作者姓名    修改时间    版本号       描述
 */
public class ProxyTest {
    public static void main(String[] args) {
        //1.创建真实对象
        Lenovo lenovo = new Lenovo();
        /**
         * 这个时候我发现我不想执行这个sale了,我想改变一点自己的花样
         * 这时候就可以用动态代理进行增强lenovo对象
         * 第一个参数为对象类加载器,因为这个代理对象也需要进行执行,代理对象和真实对象用一样的类加载器(固定写法)
         * 第二个参数为真实对象实现的接口(固定写法)
         * 第三个对象为InvocationHandler,用匿名类来进行创建,(基本上也是固定写法)
         * 返回的对象proxy,代表的就是代理对象。因为这里知道它们实现的是SaleComputer接口,故可以强转为该接口
         * 所以proxy 和 lenovo 都实现了SaleComputer 所以将来调用方法的时候,就可以不用lenovo进行调用了
         * invoke代表的是代理逻辑编写的方法:代理对象调用的所有的方法都会触发该方法执行
         */
        SaleComputer proxy = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            /**
             * 参数 1:1.proxy:代理对象
             * 参数 2:Method对象 每当通过代理对象调用方法的时候,这个方法会被封装成为一个对象,传到这里面来。方法封装对象会成为Method(反射中有讲)
             * 参数 3:代理对象调用方法时,传递的参数封装到args数组里面
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /*System.out.println("该方法执行了...");
                String name = method.getName();
                System.out.println(name);
                System.out.println(args[0]);*/

                //因为我就想改sale这个方法的参数列表
                if (method.getName().equals("sale")) {
                    double value =  (double)args[0] * 0.85;
                    System.out.println("专车接你");
                    String obj = (String) method.invoke(lenovo, value);
                    System.out.println("送货上门");
                    //增强返回值
                    return obj + "加鼠标";
                } else {
                    //使用真实对象进行调用该方法(代表lenovo对象调用method这个方法,参数是args,返回值为obj)
                    String obj = (String) method.invoke(lenovo, args);
                    return obj;
                }
            }
        });

        //2.调用方法
        String computer = proxy.sale(8000);
        System.out.println(computer);
        //proxy.show();

    }
}

Filter过滤非法字符:
在这里插入图片描述
这里真实对象是req 动态代理对象是proxy_req;放行的是proxy_req,在invoke中(这个方法一定执行的)如果是别的方法,那么就直接执行req中的方法,
在这里插入图片描述如果是getParameter则执行动态代理类proxy_req里面的方法,如果值为空,则直接返回,如果不为空,则对字符串进行替换,把笨蛋替换为***,替换完成后再返回。以达到过滤字符的目的。

Listener :监听器

概念:web的三大组件之一:servlet Filter Listener
事件监听机制:
事件: 一件事情
事件源: 事件发生的地方
监听器:一个对象
注册监听:将事件、事件源、监听器绑定在一起。当事件源发生某个事件后,执行监听器代码。
步骤:
1. 定义一个类,实现ServletContextListener接口(对于他来说,服务器就创建ServletContext,启动接听)
2. 复写方法
3. 配置
1. web.xml

cn.itcast.web.listener.ContextLoaderListener

里面的数据是实现接口类的路径
同时,指定初始化参数
2.@WebListener
监听器一般是用户在web xml配置context-param,然后,服务器启动,创建监听器,加载用户配置内容。(在spring中会有使用)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值