JavaWeb-FilterListener

news2024/9/20 22:33:37

JavaWeb-Filter&Listener

1,Filter

1.1 Filter概述

Filter 表示过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。

过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。

如下图所示,浏览器可以访问服务器上的所有的资源(servlet、jsp、html等)
在这里插入图片描述

而在访问到这些资源之前可以使过滤器拦截来下,也就是说在访问资源之前会先经过 Filter,如下图
在这里插入图片描述

1.2 Filter快速入门

1.2.1 开发步骤

进行 Filter 开发分成以下三步实现

  • 定义类,实现 Filter接口,并重写其所有方法
    在这里插入图片描述

  • 配置Filter拦截资源的路径:在类上定义 @WebFilter 注解。而注解的 value 属性值 /* 表示拦截所有的资源
    在这里插入图片描述

  • 在doFilter方法中输出一句话,并放行
    在这里插入图片描述

    上述代码中的 chain.doFilter(request,response); 就是放行,也就是让其访问本该访问的资源。

1.2.2 代码演示

创建一个项目,项目下有一个 hello.jsp 页面,项目结构如下:
在这里插入图片描述

pom.xml 配置文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>filter-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>80</port>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

hello.jsp 页面内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>hello JSP~</h1>
</body>
</html>

我们现在在浏览器输入 http://localhost/filter-demo/hello.jsp 访问 hello.jsp 页面,这里是可以访问到 hello.jsp 页面内容的。
在这里插入图片描述

接下来编写过滤器。过滤器是 Web 三大组件之一,所以我们将 filter 创建在 com.itheima.web.filter 包下,起名为 FilterDemo

@WebFilter("/*")
public class FilterDemo implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("FilterDemo...");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }
}

重启启动服务器,再次重新访问 hello.jsp 页面,这次发现页面没有任何效果,但是在 idea 的控制台可以看到如下内容
在这里插入图片描述

上述效果说明 FilterDemo 这个过滤器的 doFilter() 方法执行了,但是为什么在浏览器上看不到 hello.jsp 页面的内容呢?这是因为在 doFilter() 方法中添加放行的方法才能访问到 hello.jsp 页面。那就在 doFilter() 方法中添加放行的代码

//放行
 chain.doFilter(request,response);

再次重启服务器并访问 hello.jsp 页面,发现这次就可以在浏览器上看到页面效果。

FilterDemo 过滤器完整代码如下:

@WebFilter("/*")
public class FilterDemo implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("1.FilterDemo...");
        //放行
        chain.doFilter(request,response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }
}

1.3 Filter执行流程

在这里插入图片描述

如上图是使用过滤器的流程,我们通过以下问题来研究过滤器的执行流程:

  • 放行后访问对应资源,资源访问完成后,还会回到Filter中吗?

    从上图就可以看出肯定 回到Filter中

  • 如果回到Filter中,是重头执行还是执行放行后的逻辑呢?

    如果是重头执行的话,就意味着 放行前逻辑 会被执行两次,肯定不会这样设计了;所以访问完资源后,会回到 放行后逻辑,执行该部分代码。

通过上述的说明,我们就可以总结Filter的执行流程如下:
在这里插入图片描述

接下来我们通过代码验证一下,在 doFilter() 方法前后都加上输出语句,如下
在这里插入图片描述

同时在 hello.jsp 页面加上输出语句,如下
在这里插入图片描述

执行访问该资源打印的顺序是按照我们标记的标号进行打印的话,说明我们上边总结出来的流程是没有问题的。启动服务器访问 hello.jsp 页面,在控制台打印的内容如下:
在这里插入图片描述

以后我们可以将对请求进行处理的代码放在放行之前进行处理,而如果请求完资源后还要对响应的数据进行处理时可以在放行后进行逻辑处理。

1.4 Filter拦截路径配置

拦截路径表示 Filter 会对请求的哪些资源进行拦截,使用 @WebFilter 注解进行配置。如:@WebFilter("拦截路径")

拦截路径有如下四种配置方式:

  • 拦截具体的资源:/index.jsp:只有访问index.jsp时才会被拦截
  • 目录拦截:/user/*:访问/user下的所有资源,都会被拦截
  • 后缀名拦截:*.jsp:访问后缀名为jsp的资源,都会被拦截
  • 拦截所有:/*:访问所有资源,都会被拦截

通过上面拦截路径的学习,大家会发现拦截路径的配置方式和 Servlet 的请求资源路径配置方式一样,但是表示的含义不同。

1.5 过滤器链

1.5.1 概述

过滤器链是指在一个Web应用,可以配置多个过滤器,这多个过滤器称为过滤器链。

如下图就是一个过滤器链,我们学习过滤器链主要是学习过滤器链执行的流程
在这里插入图片描述

上图中的过滤器链执行是按照以下流程执行:

  1. 执行 Filter1 的放行前逻辑代码
  2. 执行 Filter1 的放行代码
  3. 执行 Filter2 的放行前逻辑代码
  4. 执行 Filter2 的放行代码
  5. 访问到资源
  6. 执行 Filter2 的放行后逻辑代码
  7. 执行 Filter1 的放行后逻辑代码

以上流程串起来就像一条链子,故称之为过滤器链。

1.5.2 代码演示

  • 编写第一个过滤器 FilterDemo ,配置成拦截所有资源

    @WebFilter("/*")
    public class FilterDemo implements Filter {
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
            //1. 放行前,对 request数据进行处理
            System.out.println("1.FilterDemo...");
            //放行
            chain.doFilter(request,response);
            //2. 放行后,对Response 数据进行处理
            System.out.println("3.FilterDemo...");
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void destroy() {
        }
    }
    
  • 编写第二个过滤器 FilterDemo2 ,配置炒年糕拦截所有资源

    @WebFilter("/*")
    public class FilterDemo2 implements Filter {
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
            //1. 放行前,对 request数据进行处理
            System.out.println("2.FilterDemo...");
            //放行
            chain.doFilter(request,response);
            //2. 放行后,对Response 数据进行处理
            System.out.println("4.FilterDemo...");
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void destroy() {
        }
    }
    
    
  • 修改 hello.jsp 页面中脚本的输出语句

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1>hello JSP~</h1>
        <%
            System.out.println("3.hello jsp");
        %>
    </body>
    </html>
    
  • 启动服务器,在浏览器输入 http://localhost/filter-demo/hello.jsp 进行测试,在控制台打印内容如下
    在这里插入图片描述

    从结果可以看到确实是按照我们之前说的执行流程进行执行的。

1.5.3 问题

上面代码中为什么是先执行 FilterDemo ,后执行 FilterDemo2 呢?

我们现在使用的是注解配置Filter,而这种配置方式的优先级是按照过滤器类名(字符串)的自然排序。

比如有如下两个名称的过滤器 : BFilterDemoAFilterDemo 。那一定是 AFilterDemo 过滤器先执行。

1.6 案例

1.6.1 需求

访问服务器资源时,需要先进行登录验证,如果没有登录,则自动跳转到登录页面

1.6.2 分析

我们要实现该功能是在每一个资源里加入登陆状态校验的代码吗?显然是不需要的,只需要写一个 Filter ,在该过滤器中进行登陆状态校验即可。而在该 Filter 中逻辑如下:
在这里插入图片描述

1.6.3 代码实现

1.6.3.1 创建Filter

brand-demo 工程创建 com.itheima.web.filter 包,在该下创建名为 LoginFilter 的过滤器

@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
      
    }

    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }
}
1.6.3.2 编写逻辑代码

doFilter() 方法中编写登陆状态校验的逻辑代码。

我们首先需要从 session 对象中获取用户信息,但是 ServletRequest 类型的 requset 对象没有获取 session 对象的方法,所以此时需要将 request对象强转成 HttpServletRequest 对象。

HttpServletRequest req = (HttpServletRequest) request;

然后完成以下逻辑

  • 获取Session对象
  • 从Session对象中获取名为 user 的数据
  • 判断获取到的数据是否是 null
    • 如果不是,说明已经登陆,放行
    • 如果是,说明尚未登陆,将提示信息存储到域对象中并跳转到登陆页面

代码如下:

@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest req = (HttpServletRequest) request;
   
        //1. 判断session中是否有user
        HttpSession session = req.getSession();
        Object user = session.getAttribute("user");

        //2. 判断user是否为null
        if(user != null){
            // 登录过了
            //放行
            chain.doFilter(request, response);
        }else {
            // 没有登陆,存储提示信息,跳转到登录页面

            req.setAttribute("login_msg","您尚未登陆!");
            req.getRequestDispatcher("/login.jsp").forward(req,response);
        }
    }

    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }
}
1.6.3.3 测试并抛出问题

在浏览器上输入 http://localhost:8080/brand-demo/ ,可以看到如下页面效果
在这里插入图片描述

从上面效果可以看出没有登陆确实是跳转到登陆页面了,但是登陆页面为什么展示成这种效果了呢?

1.6.3.4 问题分析及解决

因为登陆页面需要 css/login.css 这个文件进行样式的渲染,下图是登陆页面引入的css文件图解
在这里插入图片描述

而在请求这个css资源时被过滤器拦截,就相当于没有加载到样式文件导致的。解决这个问题,只需要对所以的登陆相关的资源进行放行即可。还有一种情况就是当我没有用户信息时需要进行注册,而注册时也希望被过滤器放行。

综上,我们需要在判断session中是否包含用户信息之前,应该加上对登陆及注册相关资源放行的逻辑处理

//判断访问资源路径是否和登录注册相关
//1,在数组中存储登陆和注册相关的资源路径
String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet","/register.jsp","/registerServlet","/checkCodeServlet"};
//2,获取当前访问的资源路径
String url = req.getRequestURL().toString(); 

//3,遍历数组,获取到每一个需要放行的资源路径
for (String u : urls) {
    //4,判断当前访问的资源路径字符串是否包含要放行的的资源路径字符串
    /*
    	比如当前访问的资源路径是  /brand-demo/login.jsp
    	而字符串 /brand-demo/login.jsp 包含了  字符串 /login.jsp ,所以这个字符串就需要放行
    */
    if(url.contains(u)){
        //找到了,放行
        chain.doFilter(request, response);
        //break;
        return;
    }
}
1.6.3.5 过滤器完整代码
@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest req = (HttpServletRequest) request;
        
        //判断访问资源路径是否和登录注册相关
        //1,在数组中存储登陆和注册相关的资源路径
        String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet","/register.jsp","/registerServlet","/checkCodeServlet"};
        //2,获取当前访问的资源路径
        String url = req.getRequestURL().toString(); 

        //3,遍历数组,获取到每一个需要放行的资源路径
        for (String u : urls) {
            //4,判断当前访问的资源路径字符串是否包含要放行的的资源路径字符串
            /*
                比如当前访问的资源路径是  /brand-demo/login.jsp
                而字符串 /brand-demo/login.jsp 包含了  字符串 /login.jsp ,所以这个字符串就需要放行
            */
            if(url.contains(u)){
                //找到了,放行
                chain.doFilter(request, response);
                //break;
                return;
            }
        }
   
        //1. 判断session中是否有user
        HttpSession session = req.getSession();
        Object user = session.getAttribute("user");

        //2. 判断user是否为null
        if(user != null){
            // 登录过了
            //放行
            chain.doFilter(request, response);
        }else {
            // 没有登陆,存储提示信息,跳转到登录页面

            req.setAttribute("login_msg","您尚未登陆!");
            req.getRequestDispatcher("/login.jsp").forward(req,response);
        }
    }

    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }
}

2,Listener

2.1 概述

  • Listener 表示监听器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。

  • 监听器可以监听就是在 applicationsessionrequest 三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。

    request 和 session 我们学习过。而 applicationServletContext 类型的对象。

    ServletContext 代表整个web应用,在服务器启动的时候,tomcat会自动创建该对象。在服务器关闭时会自动销毁该对象。

2.2 分类

JavaWeb 提供了8个监听器:
在这里插入图片描述

这里面只有 ServletContextListener 这个监听器后期我们会接触到,ServletContextListener 是用来监听 ServletContext 对象的创建和销毁。

ServletContextListener 接口中有以下两个方法

  • void contextInitialized(ServletContextEvent sce)ServletContext 对象被创建了会自动执行的方法
  • void contextDestroyed(ServletContextEvent sce)ServletContext 对象被销毁时会自动执行的方法

2.3 代码演示

我们只演示一下 ServletContextListener 监听器

  • 定义一个类,实现ServletContextListener 接口
  • 重写所有的抽象方法
  • 使用 @WebListener 进行配置

代码如下:

@WebListener
public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //加载资源
        System.out.println("ContextLoaderListener...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //释放资源
    }
}

启动服务器,就可以在启动的日志信息中看到 contextInitialized() 方法输出的内容,同时也说明了 ServletContext 对象在服务器启动的时候被创建了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA性能统计项目

一、项目背景&#xff1a;我们希望设计开发一个小的框架&#xff0c;能够获取接口调用的各种统计信息&#xff0c;比如&#xff0c;响应时间的最大值&#xff08;max&#xff09;、最小值&#xff08;min&#xff09;、平均值&#xff08;avg&#xff09;、百分位值&#xff08…

力扣OJ(2000+)

目录 2032. 至少在两个数组中出现的值 2037. 使每位学生都有座位的最少移动次数 2042. 检查句子中的数字是否递增 2097. 合法重新排列数对 2180. 统计各位数字之和为偶数的整数个数 2185. 统计包含给定前缀的字符串 2283. 判断一个数的数字计数是否等于数位的值 2287. …

基于MBD 的软件品质保证技术

基于MBD的软件是什么&#xff1f; 基于MBD的软件是基于模型开发的软件&#xff0c;主要应用于汽车、电子电气、机器人、航空、航天等行业。 ​​​ 与使用现有代码开发程序的方法不同&#xff0c;MBD 方法包括首先开发模型&#xff0c;将模型转换为代码&#xff0c;然后基于转换…

Ansys Speos | 2023R1 动态仿真助力车灯早期优化

前言 光学仿真是产品设计师应用的关键工具之一&#xff0c;能让用户在制作物理原型之前就通过数字环境体验产品。这对汽车领域来说显得尤为重要&#xff0c;随着汽车照明功能&#xff08;如转向指示灯&#xff09;越来越生动&#xff0c;TIER-1 需要能够在样件前&#xff0c;通…

Mac安装android studio

1. 下载as 下载地址 2. 安装 3. 启动软件 4.创建新项目 选择空白活动 名字为FirstApp&#xff0c;语言选择java 等待项目加载完毕 项目加载完毕 5.创建设备 6. 启动项目

【28】C语言 | 关于指针练习(1)

目录 1、下列输出什么 2、计算求和 3、使用指针打印数组内容 4、打印水仙花数 5、写一个函数&#xff0c;可以逆序一个字符串的内容 6、题目名称:打印菱形 6、喝汽水 7、猜名次 8、下列关于整形数组输出什么并解释 9、下列关于字符数组输出什么并解释 9.1 下列关于字…

【C++详解】——初识STL(string类的使用)

&#x1f4d6; 前言&#xff1a;STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且一个包罗数据结构与算法的软件框架。 目录&#x1f552; 1. string 概述&#x1f552; 2. 标准库中的stri…

【小程序 | 黑马优选】tabBar、首页制作

文章目录tabBar制作首页制作配置网络请求制作轮播图效果渲染轮播图的UI解构配置小程序分包点击轮播图跳转到商品详情页面封装 uni.$showMsg() 方法分类导航区制作楼层区域制作tabBar制作 在 pages 目录中&#xff0c;创建首页(home)、分类(cate)、购物车(cart)、我的(my) 这 4…

windows下zookeeper搭建

程序包下载 官网下载地址 下载解压后如下&#xff01; 注意&#xff0c;zookeeper需要java环境&#xff0c;如果配置了JAVA_HOME那最好&#xff0c;如果没配置就会出现点击bin下的zkServer.cmd后CMD窗口一闪而过 修改配置 如果本地端口没有特别要求可以直接复制conf下的zo…

多臂PEG衍生物8-Arm PEG-SAA,8-Arm PEG-Succinamide Acid,八臂PEG丁二酸酰胺

一&#xff1a;产品描述 1、名称 英文&#xff1a;8-Arm PEG-SAA&#xff0c;8-Arm PEG-Succinamide Acid 中文&#xff1a;八臂-聚乙二醇-丁二酸酰胺 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Carboxylic acid PEG Multi-arm PEGs 4、分子量&#xff1a;可定制…

IDEA新建js项目(hello)和执行js脚本

一)、安装Node.js具体操作参考:https://blog.csdn.net/xijinno1/article/details/128774375二)、IDEA中新建js项目(hello world)1.按照下图&#xff0c;新建js项目2.选中示例代码文件后点击鼠标右键&#xff0c;选中菜单栏中的 运行* 栏目运行代码(第一次运行代码的方式)3.若是…

【版本控制】Git快速上手

Do you know what Git is&#xff1f; 一.引入 (1) 作用 Git 是一个分布式版本控制系统&#xff0c;主要是用于管理开发过程中的源代码文件&#xff08;Java类&#xff0c;xml文件&#xff0c;html页面等&#xff09;。可用于代码回溯&#xff0c;版本切换&#xff0c;多人协作…

AcWing 292. 炮兵阵地(状态压缩DP)

AcWing 292. 炮兵阵地&#xff08;状态压缩DP&#xff09;一、题目二、思路1、分析2、状态表示3、状态转移4、循环设计5、初末状态三、代码一、题目 二、思路 1、分析 这道题的话和我们之前讲解的AcWing 327. 玉米田&#xff08;状态压缩DP&#xff09;和AcWing 1064. 小国王…

Jenkins环境搭建与实战

Jenkins环境搭建与实战1、Jenkins2、GItLab的安装2.1、安装依赖2.1.1、CentOS8安装报错2.1.2、找不到对应包安装报错2.2、配置镜像2.3、安装gitlab3、安装Jenkins4、Maven安装4.1、出现报错 The JAVA_HOME environment variable is not defined correctly的错误5、Jenkins 通过…

SWIFT Framework .NET 2023

SWIFT Framework .NET 2023 Latest 2023 specification messages.Improves parsing..NET Framework 4.8 release.Performance updates.Improves handling of special characters. SWIFT Framework.NET是一个用于在组织信息系统基础架构中捕获、验证和处理SWIFT消息的系统。SWI…

3.5主存储器与CPU的连接

文章目录一、引子二、单块存储芯片与CPU的连接三、多块存储芯片与CPU的连接1.现代计算机2.命名3.增加主存的存储字长--位扩展&#xff08;1&#xff09;单块&#xff08;2&#xff09;多块4.增加主存的存储字数--字扩展&#xff08;1&#xff09;单块&#xff08;2&#xff09;…

19行列式公式和代数余子式

行列式公式 学习了关于行列式的这么多性质&#xff0c;现在我们有能力推导二阶行列式公式了&#xff1a; 观察上面的推导过程&#xff0c;不难发现&#xff0c;行列式的值等于使用性质3.b 分解后所得的那些非零行列式的和&#xff0c;所谓的非零行列式也即该行列式各行各列都…

【算法基础】大整数加减乘除法(高精度)

大整数的思想:用数组存储大整数(超长整数),比如存储1000位的整数只需要开辟一个长度为1000的数组(C++通常使用vector),今天将通过OJ例题来介绍高精度问题。(完全0基础的先建议自主学习一下,本博客默认已了解大致思想) 一、 大整数加法(大整数 + 大整数) (一)Qu…

6 逻辑斯蒂回归

文章目录回归问题和分类问题问题提出逻辑回归二分类问题逻辑函数与线性回归方程的不同模型变化loss函数不同BCEloss函数的介绍课程代码课程来源&#xff1a; 链接课程文本来源借鉴&#xff1a; 链接以及&#xff08;强烈推荐&#xff09; Birandaの回归问题和分类问题 有监督学…

Docker安装Tomcat服务器

Docker安装Tomcat服务器查看tomcat镜像下载 tomcat镜像启动tomcat容器浏览器访问容器中的tomcat1 查看ip2 查看容器是否启动3 进入容器重启容器浏览器访问查看tomcat镜像 docker search tomcat下载 tomcat镜像 咱直接下载最近版本的tomcat镜像 docker pull tomcat查看一下本…