javaweb基础:过滤器Filter

现在聊一下javaweb三大核心(servlet 程序 Listener 监听器 Filter过滤器)之一的过滤器。

过滤器本身也是javaee中的一个接口,而其主要作用就是:拦截请求,过滤响应。

用一个不十分恰当的例子来说,过滤器像是净水器中过滤芯片,水过滤片的时候都会被拦住,然后将水中杂质给阻挡到出水口,而纯净的水可以通过。

所以网站很多时候通过过滤器进行权限检查比如最常见的购物网站,如果不登陆就无法打开购物车等。

产生原因

这个就有一个问题,不是jsp支持编程脚本吗,我直接如下判断不就行了:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>Title</title>
</head>
<body>
 <%
  String name= (String) session.getAttribute("username");
  if (name!=null && name.equals("")){
        request.getRequestDispatcher("/资源路径").forward(request,response);
  }
 %>
</body>

</html>

完美解决了问题了,这样干嘛还需要写一个过滤器,还需与实现过滤器接口,然后创建一个java文件呢。但是不要忽略一点,对于忘了url请求不单单是jsp,比如图片,还有pdf等格式,你如何在其内容中写java编程呢。

现在又有一个大胆的想法了,既然如此那我可以在servlet中判断一下不久可以了。还是这个例子似乎解决如下:

@WebServlet("/test")
public class test extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name= (String) req.getSession().getAttribute("username");
        if (name!=null && name.equals("")){
//            通过service服务逻辑得到资源
        }else{
            req.getRequestDispatcher("/登录路径").forward(req,resp);
        }
    }

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

虽然可以解决问题,但是有一个问题,那就是需要在每个servlet中写一遍是不是很麻烦,当然还有一种写法,就是写一个baseservlet,在这个方法里面写,然后其它的方法继承这个方法:

baseservlet类文件如下:

public class baseservlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        url 对方法进行重新命名,而不适用dopsost或者doget
        String action = req.getParameter("action");
        try {
            String name = (String) req.getSession().getAttribute("username");
            if (name != null && name.equals("")) {
                Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
                method.invoke(this, req, resp);
            } else {
                req.getRequestDispatcher("/登录路径").forward(req, resp);
            }

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

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

    }
}

然后写一个继承类即可不过这样写也有问题,虽然可以解决问题,但是有时候发现这个过滤会有大量需求,那么为什么不提炼处理一个类,然后直接调用即可,没必要每次都手写一个,而且还有一个就是对于一些拦截的路径也可以使用类似正则表达式的方式拦截。

所以过滤器这个东西的意义就有了,比如在配置的时候:

    <filter>
        <filter-name>testFilters</filter-name>
        <filter-class>com.test.testFilters</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>testFilters</filter-name>
<!--        这个是拦截本项目下的所有请求  , /test/* 表示拦截 项目/test  路径的所有请求-->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

初体验

先看一个资源:

在这里插入图片描述

然后写一个过滤器,然后配置:

package com.test;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

public class testFilters implements Filter {
    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest httprequest= (HttpServletRequest) request;
        String name = (String) httprequest.getSession().getAttribute("username");
        System.out.println(name);
        if (name != null && !(name.equals(""))) {
            chain.doFilter(request,response);
        } else {
            response.setContentType("text/html; charset=UTF-8");
            response.getWriter().write("请登录才行");
        }
    }
}

然后进行配置:

   <filter>
        <filter-name>testFilters</filter-name>
        <filter-class>com.test.testFilters</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>testFilters</filter-name>
<!--        这个是拦截本项目下的所有请求  , /test 表示拦截 项目/test  路径的所有请求-->
        <url-pattern>/jpg/*</url-pattern>
    </filter-mapping>

然后访问地址:http://localhost:8080/javaweb/jpg/gu.png(这是我的地址,自己使用自己的地址)

结果如下:

在这里插入图片描述

如果再写一个log.jsp,只会简单将其赋值一个session中的属性值而已

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>Title</title>
</head>
<body>
 <%
// 我们直接通过脚本赋值给session属性
  session.setAttribute("username","张三");
 %>
</body>
</html>

然后先访问:http://localhost:8080/javaweb/test.jsp

然后在访问:http://localhost:8080/javaweb/jpg/gu.png

在这里插入图片描述

所以可以看出过滤器的作用了,其就是对请求的路径进行一次筛选,符合的就让进去,不符合的就拒绝。(感觉像火车站的是检票员)

过滤器的生命周期

其实和servlet很相似,现在说一下其生命周期:

先看一下过滤器有什么方法:

在这里插入图片描述

然后如下尝试:

public class testFilters implements Filter {
    public testFilters(){
        System.out.println("构造方法");
    }
    public void init(FilterConfig config) throws ServletException {
        System.out.println("初始化了");
    }

    public void destroy() {
        System.out.println("销毁了");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        System.out.println("过滤器启动了");
    }
}

然后启动tomcat后:

在这里插入图片描述

然后访问(因为配置的还是前面演示的配置):http://localhost:8080/javaweb/jpg/gu.png 多访问几次

在这里插入图片描述

关闭tomcat:

在这里插入图片描述

所以总结:

  • 第一步: 构造方法(这个不用多说,毕竟为面向对象的java)
  • 第二步:初始化方法(init), 这前俩步在web工程启动的时候就执行了。
  • 第三步:过滤方式(doFilter): 每次拦截的请求的时候就会执行。
  • 第四步:销毁(destroy):停止web工程的时候就会执行。

FilterConfig

这个看名字就知道,是过滤器的配置对象类,有点像是servletConfig对象相似,只不过一个是服务于servlet一个服务于filter。

而且FilterConfig也是在Filter创建的时候同时就会创建一个FilterConfig类的,这里包含了Filter配置文件的配置信息。

看官网文档:

在这里插入图片描述

而创建的时候,都是在filter中初始化init方法中得到Filterconfig:

public void init(FilterConfig config) throws ServletException {
       
    }

对于FilterConfig的方法然后如下尝试:

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_4_0.xsd"
         version="4.0">
    <context-param>
        <param-name>contextname</param-name>
        <param-value>全局数据</param-value>
    </context-param>
    <filter>
        <filter-name>testFilters</filter-name>
        <filter-class>com.test.testFilters</filter-class>
        <init-param>
            <param-name>test</param-name>
            <param-value>小三</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>testFilters</filter-name>
<!--        这个是拦截本项目下的所有请求  , /test 表示拦截 项目/test  路径的所有请求-->
        <url-pattern>/jpg/*</url-pattern>
    </filter-mapping>
</web-app>   

然后看一下过滤器:

public class testFilters implements Filter {


    public void init(FilterConfig config) throws ServletException {
        System.out.println("getFilterName()  初始化了"+config.getFilterName());
        System.out.println("getServletContext()初始化了"+config.getServletContext().getInitParameter("contextname"));
        System.out.println("getInitParameter() 初始化了"+config.getInitParameter("test"));
        System.out.println("getInitParameterNames()初始化了"+config.getInitParameterNames());
    }

    public void destroy() {
 
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
 
    }
}

在这里插入图片描述

其实看起和servletconfig很相似,也就是可以得到配置文件web.xml配置的信息。

多个过滤器套用

其实有时候过滤器不是单独一个使用,而是可以套用的,为什么这样说呢?看下官网文档:

在这里插入图片描述

可以看出过滤器器首先会查看是否有下一个过滤器,如果没有再返回过滤器判断之后的资源。先不说这个而是做一个测试。

首先写两个过滤器:

testFilters.java

public class testFilters implements Filter {


    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
        System.out.println("销毁了");
    }

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

testFilters1.java

public class testFilters1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

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

    @Override
    public void destroy() {

    }
}

不过先说一下在配置的web.xml :

先配置 testFilterstestFilters1前面。

<?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_4_0.xsd"
         version="4.0">

    <filter>
        <filter-name>testFilters</filter-name>
        <filter-class>com.test.testFilters</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>testFilters</filter-name>
<!--        这个是拦截本项目下的所有请求  , /test 表示拦截 项目/test  路径的所有请求-->
        <url-pattern>/jpg/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>testFilters1</filter-name>
        <filter-class>com.test.testFilters1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>testFilters1</filter-name>
        <!--        这个是拦截本项目下的所有请求  , /test 表示拦截 项目/test  路径的所有请求-->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

结果如下:

在这里插入图片描述

如果先配置 testFilters1testFilters前面。

<?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_4_0.xsd"
         version="4.0">

    
      <filter>
        <filter-name>testFilters1</filter-name>
        <filter-class>com.test.testFilters1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>testFilters1</filter-name>
        <!--        这个是拦截本项目下的所有请求  , /test 表示拦截 项目/test  路径的所有请求-->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    
    <filter>
        <filter-name>testFilters</filter-name>
        <filter-class>com.test.testFilters</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>testFilters</filter-name>
<!--        这个是拦截本项目下的所有请求  , /test 表示拦截 项目/test  路径的所有请求-->
        <url-pattern>/jpg/*</url-pattern>
    </filter-mapping>

</web-app>

在这里插入图片描述

其实可以看出,在使用多个过滤器的时候,谁在前是根据web.xml配置文件中<filter-mapping\>前就先运行。

而根据 testFilterstestFilters1前面,画一个图:

在这里插入图片描述

所以总结:

  • 执行下一个filter过滤器(如果有filter)
  • 执行目标资源(如果没有filter)

同时还有一个补充点那就是:

  • 所有的filter和目标资源默认都是执行在同一个线程中。
  • 多个filter共同执行的时候,他们使用的是同一个request对象。

路径配置补充

  • 精确匹配

    <url-pattern>/jpg/gu.png</url-pattern>
    

    只会过滤http://localhost:8080/javaweb/jpg/gu.png,其它的地址不会过滤。

  • 目标匹配

    <url-pattern>/jpg/*</url-pattern>
    

    这个会过滤:以 http://localhost:8080/javaweb/jpg/ 开始的所有url ,其它的地址不会过滤。

  • 后缀匹配

    <url-pattern>*.do</url-pattern>
    

    过滤以do结尾的请求地址,还有一点要注意那就是在* 前不要有/.

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...