mvc中web层filter过滤器和listener监听器的详解

web层

Filter

Web核心中一共涉及的三大技术
Servlet : server applet 服务器端的小程序(主要处理请求响应的部分)
Filter: 过滤器 , 过滤: 将自己所需要的内容留下 不需要的内容剔除
Listener : 监听器

概述

在这里插入图片描述

需要配置web.xml(重点)

filter创建步骤

  1. 创建类
  2. 实现javax.servlet.Filter的规范(接口)
/**
 * 1. 创建类
 * 2. 实现javax.servlet.Filter的规范(接口)
 * 3. 通知tomcat我们配置了过滤器 (web.xml进行配置)
 * 4. 正常编写过滤器的代码即可
 */
public class HelloFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * 执行过滤的方法 , 每次执行到过滤器都会执行此方法 相当于servlet的service方法
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("HelloFilter 被执行了 A");
        //放行代码
        filterChain.doFilter(request, response);

        System.out.println("HelloFilter 放行后的代码 B");
    }
    @Override
    public void destroy() {

    }
}

3.通知tomcat我们配置了过滤器 (配置web.xml)

<!--

     <filter> 通知tomcat我们需要增加一个过滤器
        <filter-name>HelloFilter</filter-name>  filter的名称(别名) 整个web.xml中必须唯一
        <filter-class>com.llz.web.a_filter.HelloFilter</filter-class> 过滤器的位置,全限定类名
    </filter>
    <filter-mapping> filter映射路径配置
        <filter-name>HelloFilter</filter-name> 使用别名
        <url-pattern>/DemoServlet</url-pattern> 过滤器的过滤路径
    </filter-mapping>
-->
<filter>
    <filter-name>HelloFilter</filter-name>
    <filter-class>com.llz.web.a_filter.HelloFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HelloFilter</filter-name>
    <url-pattern>/DemoServlet</url-pattern>
</filter-mapping>

4.正常编写过滤器代码即可

filter的执行流程

在这里插入图片描述
过滤器作用:全站编码,过滤非法字符……

filter的创建方式

a.配置web.xml
b.@WebFilter注解

/**
 * 1.创建类
 * 2.实现javax.servlet.Filter 接口
 * 3.配置web.xml
 *     <filter>
 *         <filter-name>XmlFilter</filter-name>
 *         <filter-class>com.llz.web.b_filter.XmlFilter</filter-class>
 *     </filter>
 *     <filter-mapping>
 *         <filter-name>XmlFilter</filter-name>
 *         <url-pattern>/DemoServlet</url-pattern>
 *     </filter-mapping>
 *     给Filter一个拦截路径
 * 4.编写放行代码
 */
@WebFilter(urlPatterns = "/DemoServlet")
public class AnnoFilter implements Filter {

过滤器的其他知识点

生命周期

含义:类什么时候创建 什么时候销毁
init :在项目启动就执行(只执行一次) : servlet默认情况下是第一次访问servlet才初始化
doFilter:每次访问只要被拦截 必然执行该方法 每一次都执行
destroy :项目停止时 执行销毁 只执行一次

拦截路径的配置

servlet的url-pattern设置 , 访问到servlet入口 (四种)
完全匹配: /a/b/c/Servlet
不完全匹配(目录匹配): /a/b/c/*
后缀名匹配: *.jsp *.html *.do *.abc
缺省匹配: / 以上三种都没有匹配成功 执行默认的缺省匹配(tomcat里面有默认设置 404 )

filter的拦截路径设置 只有三种(匹配到 就进行拦截)
完全匹配: /a/b/c/Servlet
不完全匹配(目录匹配): /a/b/c/*
后缀名匹配: *.jsp *.html *.do *.abc
filter没有缺省匹配

//放行后如果能找到访问的资源 显示
//放行后如果没有找到能够访问的资源 404
//不放行 : 不管后面资源是否能访问成功 响应是filter构成 , 白板页面

拦截方式配置(使用默认的即可)

过滤器执行时机
在这里插入图片描述
REQUEST: 默认值 , 表示请求开始就进行拦截
在这里插入图片描述
FORWARD: 请求转发进行拦截

<filter>
     <filter-name>MyDispatcherFilter</filter-name>
     <filter-class>com.llz.web.d_dispatcher.MyDispatcherFilter</filter-class>
 </filter>
 <filter-mapping>
     <filter-name>MyDispatcherFilter</filter-name>
     <url-pattern>/DemoServlet</url-pattern>
     <!--配置filter拦截的时机, 默认值: REQUEST-->
     <!--FORWARD: 请求转发时进行拦截-->
     <dispatcher>FORWARD</dispatcher>
 </filter-mapping>

在这里插入图片描述

过滤器链

服务器中允许存在多个过滤器 , 这一组过滤器 称之为叫过滤器链
过滤器链xml , 执行的顺序问题 ,xml方式中 是 web.xml自上而下 filter的配置顺序就是执行顺序
过滤器链注解Filter执行顺序问题 : 类名决定顺序

filter的创建方式优缺点

xml :
​ 优点: 配置清晰 解耦(解除耦合,代码关联度) 利于修改(所有的内容都在一起)
​ 缺点: 配置麻烦 , 要编写大量的内容 表示一段配置

注解:
​ 优点: 简单使用 不需要大量的配置
​ 缺点: 不利于修改 每个类中嵌入的注解(耦合度太高) 建议使用注解的类不应该经常修改

全站编码

/**
 * 注意事项:
 * 1.urlPatterns = "/*" 对服务器中所有的资源都进行处理
 * 2.必须有放行 不能够影响原来的业务逻辑
 * chain.doFilter(req, resp);
 */
@WebFilter(filterName = "EncodingFilter" , urlPatterns = "/*")
public class EncodingFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("过滤器执行了");
        //处理请求响应编码
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        //必须有放行 不能够影响原来的业务逻辑
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {
    }
}

filter过滤非法字符

@WebFilter(filterName = "IllEgalFilter",urlPatterns = "/*")
public class IllEgalFilter implements Filter {
    //存储脏字字符 -- 提升作用于 让多个方法都可以使用
    List<String> list = new ArrayList<String>();
    /**
     * 初始化方法: 将脏字的集合 在此处初始化 一次即可
     */
    public void init(FilterConfig config) throws ServletException {
        //存储脏字字符
        //List<String> list = new ArrayList<String>();
        list.add("大爷");
        list.add("曰");
        list.add("你 女未");
    }
    /**
     * 1.准备处理脏字的List集合 - 在init初始化的 因为只初始化一次
     * 2.在doFilter 先获得 字符
     * @param req
     * @param resp
     * @param chain
     * @throws ServletException
     * @throws IOException
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //强转对象 : 使用熟悉的对象
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)resp;

        //设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("textml;charset=utf-8");

        //1.拿到请求字符
        String username = request.getParameter("username");
        System.out.println("filter中未被处理的username:" + username);
        //2.遍历脏字的字符集
        for (String word : list) {
            //3.判断 请求的字符是否含有脏字 , 必须是每一种脏字都判断
            if(username.contains(word)){//contains 包含  大爷去跳广场舞 大爷  曰  你妹
                username = username.replaceAll(word  , "*");
            }
        }
        System.out.println("filter中已经处理的username:" + username);
        //传入的对象是我们转换以后的对象
        chain.doFilter(request, response );//放行
    }

    public void destroy() {
    }
}

上述代码问题

问题的产生 : request.getParameter(“key”) 获得的是原始的数据,执行chain.doFilter(request, response )放行时还是会将脏字带过去。(即未起到我们想让它起到的效果)
解决方法:只要复写request.getParameter 方法即可 (修改代码逻辑)
在这里插入图片描述
解决方案:tomcat已经考虑到这个问题了 所以已经提供了一个 HttpServletRequest对象的实现类。
要求 :我们创建类 必须继承 , 但是必须提供有参构造。重写我们需要执行我们自己逻辑的方法,其他方法直接调用父类方法 (这就是包装思想)

包装类代码

public class MyHttpServletRequest extends HttpServletRequestWrapper{
    //非法字符准备的数据
    List<String> list = new ArrayList<String>();
    HttpServletRequest request2 ; //在自己本类中可以使用request对象

    public MyHttpServletRequest(HttpServletRequest request) {
        super(request); //传入到父类 所有的代码 全部实现(这里不需要复写的代码 父类全部已经完成)
        this.request2 = request;
    }

    //不需要复写的代码 父类全部已经完成
    //考虑需要被复写的代码
    @Override
    public String getParameter(String name) {
        //准备数据
        list.add("大爷");
        list.add("曰");
        list.add("你 女未");

        //System.out.println("MyHttpServletRequest-->getParameter被执行了");
        if(name==null || "".equals(name)){//防止代码错误 参数传递错误
            return null; //后续代码不再执行
        }
        //获得原来的数据
        String oldParameter = request2.getParameter(name);
        if(oldParameter==null || "".equals(oldParameter)){//防止代码错误 参数传递错误
            return null; //后续代码不再执行
        }
        //逻辑代码 -> 一定是有参数 并且 已经获得请求的数据
        for (String word : list) {
            //if(oldParameter.contains(word)){
                oldParameter = oldParameter.replaceAll(word , "*");
            //}
        }
        //只要最后返回的值是处理过后的字符串 效果达到
        return oldParameter;
    }
 }

修改之前的filter类

WebFilter(filterName = "IllEgalFilter",urlPatterns = "/*")
public class IllEgalFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {
    }

    /**
     * 1.准备处理脏字的List集合 - 在init初始化的 因为只初始化一次
     * 2.在doFilter 先获得 字符
     * @param req
     * @param resp
     * @param chain
     * @throws ServletException
     * @throws IOException
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //强转对象 : 使用熟悉的对象
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)resp;
        //设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //自己定义的request 对方法进行修改了
        MyHttpServletRequest myHttpServletRequest = new MyHttpServletRequest(request);
        //传入的对象是我们转换以后的对象
        chain.doFilter(myHttpServletRequest, response );//放行
    }

    public void destroy() {
    }
}

监听器

监听: 监控 , web中的监听器, 监听三个域对象(Request,Session ,ServletContext)的一些操作
三组监听器:
第一组:监听域对象的创建和销毁的
第二组:监听域对象操作域的api ( 往域中存数据 , 删除域中数据… )
第三组: 监听域对象session的特殊数据

第一组:监听域对象的创建和销毁的
Request(结论)
创建: 请求开始就创建
销毁: 响应结束就销毁
请求转发可以共享数据 , 重定向两个请求的情况下 不能携带数据

Session:
创建:浏览器第一次访问服务器 并调用request.getSession() 创建session对象
销毁: 默认tomcat超时三十分钟销毁, 手动调用invalidate方法 ,服务器非正常关闭销毁

ServletContext(结论)
创建: 服务器启动就创建 , 初始化数据操作(只初始化一遍数据内容) , 加载一些配置文件 , 模拟数据库
销毁: 服务器关闭就销毁 , 释放资源
监听器的目标: 证明以前的结论 , 给框架做铺垫
监听的单词:Listener

ServletContextListener

1.创建一个类
2.实现一个接口 ServletContextListener( 表示当前的java就是一个监听器 )
3.通知tomcat我们有监听 配置web.xml
监听器代码

public class MyServletContextListener implements ServletContextListener {
    /**
     * context Init 初始化 : servletContext创建 立马执行该方法
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext 初始化");
    }
    /**
     * context Destroy : 监听ServletContext 销毁方法
     * @param servletContextEvent
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext 销毁");
    }
}

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--
        配置监听器
            listener : 并不是我们在调用的 , servlet和filter必须要配置访问路径
            listener 服务器的内部在调用 只需要通知tomcat就可以 不需要我们调用
            不需要配置url-pattern
    -->
    <listener><!--通知tomcat我们有一个监听器需要配置-->
        <!--监听器的位置:全限定类名-->
        <listener-class>com.llz.web.listener.MyServletContextListener</listener-class>
    </listener>   
</web-app>

作用: 利用ServletContextListener监听器在创建ServletContext域对象时完成一些想要初始化的工作或者执行自定义任务调度

ServletRequestListener

/**
 *<listener>
 *       <listener-class>com.llz.web.listener.MyServletRequestListener</listener-class>
 *   </listener>
 *
 */
public class MyServletRequestListener implements ServletRequestListener {
    /**
     * 监听request对象创建的方法
     * @param servletRequestEvent
     */
    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        System.out.println("request对象创建");
    }
    /**
     * 监听request对象销毁的方法
     * @param servletRequestEvent
     */
    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        System.out.println("request对象销毁");
    }
}

HttpSessionListener

//@WebServlet
//@WebFilter

/**
 * <listener>
 *     <listener-class>com.llz.web.listener.MyServletRequestListener</listener-class>
 * </listener>
 */
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
    /**
     *  sessionCreated : 监听session的创建
     */
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println("session的创建");
    }
    /**
     *  sessionDestroyed : 监听session的销毁
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("session的销毁");
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值